gitx.do 0.1.1 → 0.1.2
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/README.md +40 -353
- package/dist/do/logger.d.ts +50 -0
- package/dist/do/logger.d.ts.map +1 -0
- package/dist/do/logger.js +122 -0
- package/dist/do/logger.js.map +1 -0
- package/dist/{durable-object → do}/schema.d.ts +3 -3
- package/dist/do/schema.d.ts.map +1 -0
- package/dist/{durable-object → do}/schema.js +4 -3
- package/dist/do/schema.js.map +1 -0
- package/dist/do/types.d.ts +267 -0
- package/dist/do/types.d.ts.map +1 -0
- package/dist/do/types.js +62 -0
- package/dist/do/types.js.map +1 -0
- package/dist/index.d.ts +14 -469
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -483
- package/dist/index.js.map +1 -1
- package/package.json +13 -21
- package/dist/cli/commands/add.d.ts +0 -176
- package/dist/cli/commands/add.d.ts.map +0 -1
- package/dist/cli/commands/add.js +0 -979
- package/dist/cli/commands/add.js.map +0 -1
- package/dist/cli/commands/blame.d.ts +0 -259
- package/dist/cli/commands/blame.d.ts.map +0 -1
- package/dist/cli/commands/blame.js +0 -609
- package/dist/cli/commands/blame.js.map +0 -1
- package/dist/cli/commands/branch.d.ts +0 -249
- package/dist/cli/commands/branch.d.ts.map +0 -1
- package/dist/cli/commands/branch.js +0 -693
- package/dist/cli/commands/branch.js.map +0 -1
- package/dist/cli/commands/checkout.d.ts +0 -73
- package/dist/cli/commands/checkout.d.ts.map +0 -1
- package/dist/cli/commands/checkout.js +0 -725
- package/dist/cli/commands/checkout.js.map +0 -1
- package/dist/cli/commands/commit.d.ts +0 -182
- package/dist/cli/commands/commit.d.ts.map +0 -1
- package/dist/cli/commands/commit.js +0 -457
- package/dist/cli/commands/commit.js.map +0 -1
- package/dist/cli/commands/diff.d.ts +0 -464
- package/dist/cli/commands/diff.d.ts.map +0 -1
- package/dist/cli/commands/diff.js +0 -959
- package/dist/cli/commands/diff.js.map +0 -1
- package/dist/cli/commands/log.d.ts +0 -239
- package/dist/cli/commands/log.d.ts.map +0 -1
- package/dist/cli/commands/log.js +0 -535
- package/dist/cli/commands/log.js.map +0 -1
- package/dist/cli/commands/merge.d.ts +0 -106
- package/dist/cli/commands/merge.d.ts.map +0 -1
- package/dist/cli/commands/merge.js +0 -852
- package/dist/cli/commands/merge.js.map +0 -1
- package/dist/cli/commands/review.d.ts +0 -457
- package/dist/cli/commands/review.d.ts.map +0 -1
- package/dist/cli/commands/review.js +0 -558
- package/dist/cli/commands/review.js.map +0 -1
- package/dist/cli/commands/stash.d.ts +0 -157
- package/dist/cli/commands/stash.d.ts.map +0 -1
- package/dist/cli/commands/stash.js +0 -655
- package/dist/cli/commands/stash.js.map +0 -1
- package/dist/cli/commands/status.d.ts +0 -269
- package/dist/cli/commands/status.d.ts.map +0 -1
- package/dist/cli/commands/status.js +0 -492
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/commands/web.d.ts +0 -199
- package/dist/cli/commands/web.d.ts.map +0 -1
- package/dist/cli/commands/web.js +0 -697
- package/dist/cli/commands/web.js.map +0 -1
- package/dist/cli/fs-adapter.d.ts +0 -656
- package/dist/cli/fs-adapter.d.ts.map +0 -1
- package/dist/cli/fs-adapter.js +0 -1177
- package/dist/cli/fs-adapter.js.map +0 -1
- package/dist/cli/fsx-cli-adapter.d.ts +0 -359
- package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
- package/dist/cli/fsx-cli-adapter.js +0 -619
- package/dist/cli/fsx-cli-adapter.js.map +0 -1
- package/dist/cli/index.d.ts +0 -387
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -579
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/ui/components/DiffView.d.ts +0 -12
- package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
- package/dist/cli/ui/components/DiffView.js +0 -11
- package/dist/cli/ui/components/DiffView.js.map +0 -1
- package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -10
- package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
- package/dist/cli/ui/components/ErrorDisplay.js +0 -11
- package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
- package/dist/cli/ui/components/FuzzySearch.d.ts +0 -15
- package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
- package/dist/cli/ui/components/FuzzySearch.js +0 -12
- package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
- package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -10
- package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
- package/dist/cli/ui/components/LoadingSpinner.js +0 -10
- package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
- package/dist/cli/ui/components/NavigationList.d.ts +0 -14
- package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
- package/dist/cli/ui/components/NavigationList.js +0 -11
- package/dist/cli/ui/components/NavigationList.js.map +0 -1
- package/dist/cli/ui/components/ScrollableContent.d.ts +0 -13
- package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
- package/dist/cli/ui/components/ScrollableContent.js +0 -11
- package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
- package/dist/cli/ui/components/index.d.ts +0 -7
- package/dist/cli/ui/components/index.d.ts.map +0 -1
- package/dist/cli/ui/components/index.js +0 -9
- package/dist/cli/ui/components/index.js.map +0 -1
- package/dist/cli/ui/terminal-ui.d.ts +0 -85
- package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
- package/dist/cli/ui/terminal-ui.js +0 -121
- package/dist/cli/ui/terminal-ui.js.map +0 -1
- package/dist/do/BashModule.d.ts +0 -871
- package/dist/do/BashModule.d.ts.map +0 -1
- package/dist/do/BashModule.js +0 -1143
- package/dist/do/BashModule.js.map +0 -1
- package/dist/do/FsModule.d.ts +0 -612
- package/dist/do/FsModule.d.ts.map +0 -1
- package/dist/do/FsModule.js +0 -1120
- package/dist/do/FsModule.js.map +0 -1
- package/dist/do/GitModule.d.ts +0 -635
- package/dist/do/GitModule.d.ts.map +0 -1
- package/dist/do/GitModule.js +0 -784
- package/dist/do/GitModule.js.map +0 -1
- package/dist/do/GitRepoDO.d.ts +0 -281
- package/dist/do/GitRepoDO.d.ts.map +0 -1
- package/dist/do/GitRepoDO.js +0 -479
- package/dist/do/GitRepoDO.js.map +0 -1
- package/dist/do/bash-ast.d.ts +0 -246
- package/dist/do/bash-ast.d.ts.map +0 -1
- package/dist/do/bash-ast.js +0 -888
- package/dist/do/bash-ast.js.map +0 -1
- package/dist/do/container-executor.d.ts +0 -491
- package/dist/do/container-executor.d.ts.map +0 -1
- package/dist/do/container-executor.js +0 -731
- package/dist/do/container-executor.js.map +0 -1
- package/dist/do/index.d.ts +0 -53
- package/dist/do/index.d.ts.map +0 -1
- package/dist/do/index.js +0 -91
- package/dist/do/index.js.map +0 -1
- package/dist/do/tiered-storage.d.ts +0 -403
- package/dist/do/tiered-storage.d.ts.map +0 -1
- package/dist/do/tiered-storage.js +0 -689
- package/dist/do/tiered-storage.js.map +0 -1
- package/dist/do/withBash.d.ts +0 -231
- package/dist/do/withBash.d.ts.map +0 -1
- package/dist/do/withBash.js +0 -244
- package/dist/do/withBash.js.map +0 -1
- package/dist/do/withFs.d.ts +0 -237
- package/dist/do/withFs.d.ts.map +0 -1
- package/dist/do/withFs.js +0 -387
- package/dist/do/withFs.js.map +0 -1
- package/dist/do/withGit.d.ts +0 -180
- package/dist/do/withGit.d.ts.map +0 -1
- package/dist/do/withGit.js +0 -271
- package/dist/do/withGit.js.map +0 -1
- package/dist/durable-object/object-store.d.ts +0 -633
- package/dist/durable-object/object-store.d.ts.map +0 -1
- package/dist/durable-object/object-store.js +0 -1164
- package/dist/durable-object/object-store.js.map +0 -1
- package/dist/durable-object/schema.d.ts.map +0 -1
- package/dist/durable-object/schema.js.map +0 -1
- package/dist/durable-object/wal.d.ts +0 -416
- package/dist/durable-object/wal.d.ts.map +0 -1
- package/dist/durable-object/wal.js +0 -445
- package/dist/durable-object/wal.js.map +0 -1
- package/dist/mcp/adapter.d.ts +0 -772
- package/dist/mcp/adapter.d.ts.map +0 -1
- package/dist/mcp/adapter.js +0 -895
- package/dist/mcp/adapter.js.map +0 -1
- package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
- package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
- package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
- package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
- package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
- package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
- package/dist/mcp/sandbox/object-store-proxy.js +0 -30
- package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
- package/dist/mcp/sandbox/template.d.ts +0 -17
- package/dist/mcp/sandbox/template.d.ts.map +0 -1
- package/dist/mcp/sandbox/template.js +0 -71
- package/dist/mcp/sandbox/template.js.map +0 -1
- package/dist/mcp/sandbox.d.ts +0 -764
- package/dist/mcp/sandbox.d.ts.map +0 -1
- package/dist/mcp/sandbox.js +0 -1362
- package/dist/mcp/sandbox.js.map +0 -1
- package/dist/mcp/sdk-adapter.d.ts +0 -835
- package/dist/mcp/sdk-adapter.d.ts.map +0 -1
- package/dist/mcp/sdk-adapter.js +0 -974
- package/dist/mcp/sdk-adapter.js.map +0 -1
- package/dist/mcp/tools/do.d.ts +0 -32
- package/dist/mcp/tools/do.d.ts.map +0 -1
- package/dist/mcp/tools/do.js +0 -117
- package/dist/mcp/tools/do.js.map +0 -1
- package/dist/mcp/tools.d.ts +0 -548
- package/dist/mcp/tools.d.ts.map +0 -1
- package/dist/mcp/tools.js +0 -3170
- package/dist/mcp/tools.js.map +0 -1
- package/dist/ops/blame.d.ts +0 -551
- package/dist/ops/blame.d.ts.map +0 -1
- package/dist/ops/blame.js +0 -1037
- package/dist/ops/blame.js.map +0 -1
- package/dist/ops/branch.d.ts +0 -766
- package/dist/ops/branch.d.ts.map +0 -1
- package/dist/ops/branch.js +0 -950
- package/dist/ops/branch.js.map +0 -1
- package/dist/ops/commit-traversal.d.ts +0 -349
- package/dist/ops/commit-traversal.d.ts.map +0 -1
- package/dist/ops/commit-traversal.js +0 -821
- package/dist/ops/commit-traversal.js.map +0 -1
- package/dist/ops/commit.d.ts +0 -555
- package/dist/ops/commit.d.ts.map +0 -1
- package/dist/ops/commit.js +0 -826
- package/dist/ops/commit.js.map +0 -1
- package/dist/ops/merge-base.d.ts +0 -397
- package/dist/ops/merge-base.d.ts.map +0 -1
- package/dist/ops/merge-base.js +0 -691
- package/dist/ops/merge-base.js.map +0 -1
- package/dist/ops/merge.d.ts +0 -855
- package/dist/ops/merge.d.ts.map +0 -1
- package/dist/ops/merge.js +0 -1551
- package/dist/ops/merge.js.map +0 -1
- package/dist/ops/tag.d.ts +0 -247
- package/dist/ops/tag.d.ts.map +0 -1
- package/dist/ops/tag.js +0 -649
- package/dist/ops/tag.js.map +0 -1
- package/dist/ops/tree-builder.d.ts +0 -178
- package/dist/ops/tree-builder.d.ts.map +0 -1
- package/dist/ops/tree-builder.js +0 -271
- package/dist/ops/tree-builder.js.map +0 -1
- package/dist/ops/tree-diff.d.ts +0 -291
- package/dist/ops/tree-diff.d.ts.map +0 -1
- package/dist/ops/tree-diff.js +0 -705
- package/dist/ops/tree-diff.js.map +0 -1
- package/dist/pack/delta.d.ts +0 -248
- package/dist/pack/delta.d.ts.map +0 -1
- package/dist/pack/delta.js +0 -740
- package/dist/pack/delta.js.map +0 -1
- package/dist/pack/format.d.ts +0 -446
- package/dist/pack/format.d.ts.map +0 -1
- package/dist/pack/format.js +0 -572
- package/dist/pack/format.js.map +0 -1
- package/dist/pack/full-generation.d.ts +0 -612
- package/dist/pack/full-generation.d.ts.map +0 -1
- package/dist/pack/full-generation.js +0 -1378
- package/dist/pack/full-generation.js.map +0 -1
- package/dist/pack/generation.d.ts +0 -441
- package/dist/pack/generation.d.ts.map +0 -1
- package/dist/pack/generation.js +0 -707
- package/dist/pack/generation.js.map +0 -1
- package/dist/pack/index.d.ts +0 -502
- package/dist/pack/index.d.ts.map +0 -1
- package/dist/pack/index.js +0 -833
- package/dist/pack/index.js.map +0 -1
- package/dist/refs/branch.d.ts +0 -683
- package/dist/refs/branch.d.ts.map +0 -1
- package/dist/refs/branch.js +0 -881
- package/dist/refs/branch.js.map +0 -1
- package/dist/refs/storage.d.ts +0 -833
- package/dist/refs/storage.d.ts.map +0 -1
- package/dist/refs/storage.js +0 -1023
- package/dist/refs/storage.js.map +0 -1
- package/dist/refs/tag.d.ts +0 -860
- package/dist/refs/tag.d.ts.map +0 -1
- package/dist/refs/tag.js +0 -996
- package/dist/refs/tag.js.map +0 -1
- package/dist/storage/backend.d.ts +0 -425
- package/dist/storage/backend.d.ts.map +0 -1
- package/dist/storage/backend.js +0 -41
- package/dist/storage/backend.js.map +0 -1
- package/dist/storage/fsx-adapter.d.ts +0 -204
- package/dist/storage/fsx-adapter.d.ts.map +0 -1
- package/dist/storage/fsx-adapter.js +0 -518
- package/dist/storage/fsx-adapter.js.map +0 -1
- package/dist/storage/lru-cache.d.ts +0 -691
- package/dist/storage/lru-cache.d.ts.map +0 -1
- package/dist/storage/lru-cache.js +0 -813
- package/dist/storage/lru-cache.js.map +0 -1
- package/dist/storage/object-index.d.ts +0 -585
- package/dist/storage/object-index.d.ts.map +0 -1
- package/dist/storage/object-index.js +0 -532
- package/dist/storage/object-index.js.map +0 -1
- package/dist/storage/r2-pack.d.ts +0 -1257
- package/dist/storage/r2-pack.d.ts.map +0 -1
- package/dist/storage/r2-pack.js +0 -1773
- package/dist/storage/r2-pack.js.map +0 -1
- package/dist/tiered/cdc-pipeline.d.ts +0 -1888
- package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
- package/dist/tiered/cdc-pipeline.js +0 -1880
- package/dist/tiered/cdc-pipeline.js.map +0 -1
- package/dist/tiered/migration.d.ts +0 -1104
- package/dist/tiered/migration.d.ts.map +0 -1
- package/dist/tiered/migration.js +0 -1217
- package/dist/tiered/migration.js.map +0 -1
- package/dist/tiered/parquet-writer.d.ts +0 -1145
- package/dist/tiered/parquet-writer.d.ts.map +0 -1
- package/dist/tiered/parquet-writer.js +0 -1183
- package/dist/tiered/parquet-writer.js.map +0 -1
- package/dist/tiered/read-path.d.ts +0 -835
- package/dist/tiered/read-path.d.ts.map +0 -1
- package/dist/tiered/read-path.js +0 -487
- package/dist/tiered/read-path.js.map +0 -1
- package/dist/types/capability.d.ts +0 -1385
- package/dist/types/capability.d.ts.map +0 -1
- package/dist/types/capability.js +0 -36
- package/dist/types/capability.js.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -18
- package/dist/types/index.js.map +0 -1
- package/dist/types/interfaces.d.ts +0 -673
- package/dist/types/interfaces.d.ts.map +0 -1
- package/dist/types/interfaces.js +0 -26
- package/dist/types/interfaces.js.map +0 -1
- package/dist/types/objects.d.ts +0 -692
- package/dist/types/objects.d.ts.map +0 -1
- package/dist/types/objects.js +0 -837
- package/dist/types/objects.js.map +0 -1
- package/dist/types/storage.d.ts +0 -603
- package/dist/types/storage.d.ts.map +0 -1
- package/dist/types/storage.js +0 -191
- package/dist/types/storage.js.map +0 -1
- package/dist/types/worker-loader.d.ts +0 -60
- package/dist/types/worker-loader.d.ts.map +0 -1
- package/dist/types/worker-loader.js +0 -62
- package/dist/types/worker-loader.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -198
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js +0 -272
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/sha1.d.ts +0 -325
- package/dist/utils/sha1.d.ts.map +0 -1
- package/dist/utils/sha1.js +0 -635
- package/dist/utils/sha1.js.map +0 -1
- package/dist/wire/capabilities.d.ts +0 -1044
- package/dist/wire/capabilities.d.ts.map +0 -1
- package/dist/wire/capabilities.js +0 -941
- package/dist/wire/capabilities.js.map +0 -1
- package/dist/wire/path-security.d.ts +0 -157
- package/dist/wire/path-security.d.ts.map +0 -1
- package/dist/wire/path-security.js +0 -307
- package/dist/wire/path-security.js.map +0 -1
- package/dist/wire/pkt-line.d.ts +0 -345
- package/dist/wire/pkt-line.d.ts.map +0 -1
- package/dist/wire/pkt-line.js +0 -381
- package/dist/wire/pkt-line.js.map +0 -1
- package/dist/wire/receive-pack.d.ts +0 -1059
- package/dist/wire/receive-pack.d.ts.map +0 -1
- package/dist/wire/receive-pack.js +0 -1414
- package/dist/wire/receive-pack.js.map +0 -1
- package/dist/wire/smart-http.d.ts +0 -799
- package/dist/wire/smart-http.d.ts.map +0 -1
- package/dist/wire/smart-http.js +0 -945
- package/dist/wire/smart-http.js.map +0 -1
- package/dist/wire/upload-pack.d.ts +0 -727
- package/dist/wire/upload-pack.d.ts.map +0 -1
- package/dist/wire/upload-pack.js +0 -1141
- package/dist/wire/upload-pack.js.map +0 -1
|
@@ -1,689 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview R2 Tiered Storage for GitModule
|
|
3
|
-
*
|
|
4
|
-
* This module provides tiered storage support for Git objects in the GitModule,
|
|
5
|
-
* implementing a three-tier architecture:
|
|
6
|
-
*
|
|
7
|
-
* - **Hot tier**: SQLite in Durable Object (fastest access, limited capacity)
|
|
8
|
-
* - **Warm tier**: R2 loose objects (medium latency, larger capacity)
|
|
9
|
-
* - **Cold tier**: R2 packfiles (highest latency, most efficient storage)
|
|
10
|
-
*
|
|
11
|
-
* The module automatically:
|
|
12
|
-
* - Promotes frequently accessed objects to the hot tier
|
|
13
|
-
* - Demotes old/rarely accessed objects to warm/cold tiers
|
|
14
|
-
* - Supports packfile storage in R2 for efficiency
|
|
15
|
-
*
|
|
16
|
-
* @module do/tiered-storage
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* import { TieredStorage } from 'gitx.do/do'
|
|
21
|
-
*
|
|
22
|
-
* const storage = new TieredStorage({
|
|
23
|
-
* r2: env.R2_BUCKET,
|
|
24
|
-
* sql: ctx.storage.sql,
|
|
25
|
-
* prefix: 'git/objects',
|
|
26
|
-
* hotTierMaxBytes: 50 * 1024 * 1024, // 50MB in SQLite
|
|
27
|
-
* promotionThreshold: 3, // Promote after 3 accesses
|
|
28
|
-
* demotionAgeDays: 7 // Demote after 7 days without access
|
|
29
|
-
* })
|
|
30
|
-
*
|
|
31
|
-
* // Get an object (checks hot -> warm -> cold)
|
|
32
|
-
* const obj = await storage.getObject(sha)
|
|
33
|
-
*
|
|
34
|
-
* // Store an object (goes to appropriate tier based on size/frequency)
|
|
35
|
-
* await storage.putObject(sha, type, data)
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
// ============================================================================
|
|
39
|
-
// TieredStorage Class
|
|
40
|
-
// ============================================================================
|
|
41
|
-
/**
|
|
42
|
-
* TieredStorage - R2 Tiered Storage for GitModule
|
|
43
|
-
*
|
|
44
|
-
* @description
|
|
45
|
-
* Provides a three-tier storage system for Git objects optimized for
|
|
46
|
-
* Cloudflare Workers with Durable Objects:
|
|
47
|
-
*
|
|
48
|
-
* - **Hot tier**: SQLite blob storage in Durable Object
|
|
49
|
-
* - Fastest access (local to DO)
|
|
50
|
-
* - Limited capacity (50MB default)
|
|
51
|
-
* - For frequently accessed objects
|
|
52
|
-
*
|
|
53
|
-
* - **Warm tier**: R2 loose objects
|
|
54
|
-
* - Medium latency (~50-100ms)
|
|
55
|
-
* - Unlimited capacity
|
|
56
|
-
* - For recently accessed objects
|
|
57
|
-
*
|
|
58
|
-
* - **Cold tier**: R2 packfiles
|
|
59
|
-
* - Highest latency (requires packfile parsing)
|
|
60
|
-
* - Most storage efficient
|
|
61
|
-
* - For archived/rarely accessed objects
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* ```typescript
|
|
65
|
-
* const storage = new TieredStorage({
|
|
66
|
-
* r2: env.GIT_OBJECTS,
|
|
67
|
-
* sql: ctx.storage.sql,
|
|
68
|
-
* prefix: 'repos/my-repo/objects'
|
|
69
|
-
* })
|
|
70
|
-
*
|
|
71
|
-
* // Store a new object
|
|
72
|
-
* await storage.putObject('abc123...', 'blob', blobData)
|
|
73
|
-
*
|
|
74
|
-
* // Retrieve an object (auto-promotes on frequent access)
|
|
75
|
-
* const result = await storage.getObject('abc123...')
|
|
76
|
-
* console.log(`Found in ${result.tier} tier, promoted: ${result.promoted}`)
|
|
77
|
-
*
|
|
78
|
-
* // Run maintenance (demotes old objects)
|
|
79
|
-
* await storage.runMaintenance()
|
|
80
|
-
* ```
|
|
81
|
-
*/
|
|
82
|
-
export class TieredStorage {
|
|
83
|
-
r2;
|
|
84
|
-
sql;
|
|
85
|
-
prefix;
|
|
86
|
-
hotTierMaxBytes;
|
|
87
|
-
promotionThreshold;
|
|
88
|
-
demotionAgeDays;
|
|
89
|
-
hotTierMaxObjectSize;
|
|
90
|
-
autoPromote;
|
|
91
|
-
autoDemote;
|
|
92
|
-
// Statistics tracking
|
|
93
|
-
hotHits = 0;
|
|
94
|
-
warmHits = 0;
|
|
95
|
-
coldHits = 0;
|
|
96
|
-
misses = 0;
|
|
97
|
-
promotions = 0;
|
|
98
|
-
demotions = 0;
|
|
99
|
-
// Schema initialization flag
|
|
100
|
-
initialized = false;
|
|
101
|
-
/**
|
|
102
|
-
* Creates a new TieredStorage instance.
|
|
103
|
-
*
|
|
104
|
-
* @param options - Configuration options
|
|
105
|
-
*/
|
|
106
|
-
constructor(options) {
|
|
107
|
-
this.r2 = options.r2;
|
|
108
|
-
this.sql = options.sql;
|
|
109
|
-
this.prefix = options.prefix ?? 'git/objects';
|
|
110
|
-
this.hotTierMaxBytes = options.hotTierMaxBytes ?? 50 * 1024 * 1024;
|
|
111
|
-
this.promotionThreshold = options.promotionThreshold ?? 3;
|
|
112
|
-
this.demotionAgeDays = options.demotionAgeDays ?? 7;
|
|
113
|
-
this.hotTierMaxObjectSize = options.hotTierMaxObjectSize ?? 1 * 1024 * 1024;
|
|
114
|
-
this.autoPromote = options.autoPromote ?? true;
|
|
115
|
-
this.autoDemote = options.autoDemote ?? true;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Initialize the SQLite schema for tiered storage.
|
|
119
|
-
*/
|
|
120
|
-
async initialize() {
|
|
121
|
-
if (this.initialized)
|
|
122
|
-
return;
|
|
123
|
-
// Create object metadata table
|
|
124
|
-
this.sql.exec(`
|
|
125
|
-
CREATE TABLE IF NOT EXISTS git_objects_meta (
|
|
126
|
-
sha TEXT PRIMARY KEY,
|
|
127
|
-
type TEXT NOT NULL,
|
|
128
|
-
size INTEGER NOT NULL,
|
|
129
|
-
tier TEXT NOT NULL DEFAULT 'warm',
|
|
130
|
-
access_count INTEGER NOT NULL DEFAULT 0,
|
|
131
|
-
last_accessed INTEGER NOT NULL,
|
|
132
|
-
created_at INTEGER NOT NULL,
|
|
133
|
-
pack_id TEXT,
|
|
134
|
-
pack_offset INTEGER
|
|
135
|
-
)
|
|
136
|
-
`);
|
|
137
|
-
// Create hot tier blob storage table
|
|
138
|
-
this.sql.exec(`
|
|
139
|
-
CREATE TABLE IF NOT EXISTS git_objects_hot (
|
|
140
|
-
sha TEXT PRIMARY KEY,
|
|
141
|
-
type TEXT NOT NULL,
|
|
142
|
-
data BLOB NOT NULL,
|
|
143
|
-
size INTEGER NOT NULL,
|
|
144
|
-
created_at INTEGER NOT NULL
|
|
145
|
-
)
|
|
146
|
-
`);
|
|
147
|
-
// Create index for efficient queries
|
|
148
|
-
this.sql.exec(`
|
|
149
|
-
CREATE INDEX IF NOT EXISTS idx_git_objects_meta_tier
|
|
150
|
-
ON git_objects_meta(tier)
|
|
151
|
-
`);
|
|
152
|
-
this.sql.exec(`
|
|
153
|
-
CREATE INDEX IF NOT EXISTS idx_git_objects_meta_last_accessed
|
|
154
|
-
ON git_objects_meta(last_accessed)
|
|
155
|
-
`);
|
|
156
|
-
this.sql.exec(`
|
|
157
|
-
CREATE INDEX IF NOT EXISTS idx_git_objects_meta_access_count
|
|
158
|
-
ON git_objects_meta(access_count)
|
|
159
|
-
`);
|
|
160
|
-
this.initialized = true;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Get an object from the tiered storage.
|
|
164
|
-
*
|
|
165
|
-
* @description
|
|
166
|
-
* Attempts to retrieve the object from each tier in order:
|
|
167
|
-
* 1. Hot tier (SQLite)
|
|
168
|
-
* 2. Warm tier (R2 loose objects)
|
|
169
|
-
* 3. Cold tier (R2 packfiles)
|
|
170
|
-
*
|
|
171
|
-
* Automatically promotes frequently accessed objects to hotter tiers.
|
|
172
|
-
*
|
|
173
|
-
* @param sha - 40-character SHA-1 hash of the object
|
|
174
|
-
* @returns Object data with tier information, or null if not found
|
|
175
|
-
*/
|
|
176
|
-
async getObject(sha) {
|
|
177
|
-
await this.initialize();
|
|
178
|
-
// Try hot tier first (SQLite)
|
|
179
|
-
const hotResult = await this.getFromHotTier(sha);
|
|
180
|
-
if (hotResult) {
|
|
181
|
-
this.hotHits++;
|
|
182
|
-
await this.recordAccess(sha);
|
|
183
|
-
return {
|
|
184
|
-
type: hotResult.type,
|
|
185
|
-
data: hotResult.data,
|
|
186
|
-
tier: 'hot',
|
|
187
|
-
promoted: false
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
// Get metadata to determine tier
|
|
191
|
-
const meta = await this.getMetadata(sha);
|
|
192
|
-
// Try warm tier (R2 loose objects)
|
|
193
|
-
const warmResult = await this.getFromWarmTier(sha);
|
|
194
|
-
if (warmResult) {
|
|
195
|
-
this.warmHits++;
|
|
196
|
-
await this.recordAccess(sha);
|
|
197
|
-
// Check for promotion
|
|
198
|
-
let promoted = false;
|
|
199
|
-
if (this.autoPromote && meta && meta.accessCount >= this.promotionThreshold) {
|
|
200
|
-
promoted = await this.promoteToHot(sha, warmResult.type, warmResult.data);
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
type: warmResult.type,
|
|
204
|
-
data: warmResult.data,
|
|
205
|
-
tier: 'warm',
|
|
206
|
-
promoted
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
// Try cold tier (R2 packfiles)
|
|
210
|
-
if (meta?.packId && meta.packOffset !== undefined) {
|
|
211
|
-
const coldResult = await this.getFromColdTier(meta.packId, meta.packOffset);
|
|
212
|
-
if (coldResult) {
|
|
213
|
-
this.coldHits++;
|
|
214
|
-
await this.recordAccess(sha);
|
|
215
|
-
// Check for promotion
|
|
216
|
-
let promoted = false;
|
|
217
|
-
if (this.autoPromote && meta.accessCount >= this.promotionThreshold) {
|
|
218
|
-
// Promote to warm first (not hot, since it came from cold)
|
|
219
|
-
await this.promoteToWarm(sha, coldResult.type, coldResult.data);
|
|
220
|
-
promoted = true;
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
type: coldResult.type,
|
|
224
|
-
data: coldResult.data,
|
|
225
|
-
tier: 'cold',
|
|
226
|
-
promoted
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
this.misses++;
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Store an object in the tiered storage.
|
|
235
|
-
*
|
|
236
|
-
* @description
|
|
237
|
-
* Stores the object in the appropriate tier based on size and configuration:
|
|
238
|
-
* - Small, new objects go to hot tier if capacity allows
|
|
239
|
-
* - Large objects go directly to warm tier
|
|
240
|
-
*
|
|
241
|
-
* @param sha - 40-character SHA-1 hash
|
|
242
|
-
* @param type - Git object type
|
|
243
|
-
* @param data - Raw object data
|
|
244
|
-
* @returns The tier where the object was stored
|
|
245
|
-
*/
|
|
246
|
-
async putObject(sha, type, data) {
|
|
247
|
-
await this.initialize();
|
|
248
|
-
const size = data.length;
|
|
249
|
-
const now = Date.now();
|
|
250
|
-
// Check if object already exists
|
|
251
|
-
const existing = await this.getMetadata(sha);
|
|
252
|
-
if (existing) {
|
|
253
|
-
return existing.tier;
|
|
254
|
-
}
|
|
255
|
-
// Determine target tier based on size and capacity
|
|
256
|
-
let tier = 'warm';
|
|
257
|
-
if (size <= this.hotTierMaxObjectSize) {
|
|
258
|
-
const currentHotBytes = await this.getHotTierBytes();
|
|
259
|
-
if (currentHotBytes + size <= this.hotTierMaxBytes) {
|
|
260
|
-
tier = 'hot';
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
// Store in appropriate tier
|
|
264
|
-
if (tier === 'hot') {
|
|
265
|
-
await this.storeInHotTier(sha, type, data);
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
await this.storeInWarmTier(sha, type, data);
|
|
269
|
-
}
|
|
270
|
-
// Record metadata
|
|
271
|
-
this.sql.exec(`INSERT INTO git_objects_meta (sha, type, size, tier, access_count, last_accessed, created_at)
|
|
272
|
-
VALUES (?, ?, ?, ?, 0, ?, ?)`, sha, type, size, tier, now, now);
|
|
273
|
-
return tier;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Check if an object exists in any tier.
|
|
277
|
-
*
|
|
278
|
-
* @param sha - 40-character SHA-1 hash
|
|
279
|
-
* @returns True if object exists
|
|
280
|
-
*/
|
|
281
|
-
async hasObject(sha) {
|
|
282
|
-
await this.initialize();
|
|
283
|
-
const meta = await this.getMetadata(sha);
|
|
284
|
-
return meta !== null;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Delete an object from all tiers.
|
|
288
|
-
*
|
|
289
|
-
* @param sha - 40-character SHA-1 hash
|
|
290
|
-
*/
|
|
291
|
-
async deleteObject(sha) {
|
|
292
|
-
await this.initialize();
|
|
293
|
-
const meta = await this.getMetadata(sha);
|
|
294
|
-
if (!meta)
|
|
295
|
-
return;
|
|
296
|
-
// Delete from hot tier
|
|
297
|
-
this.sql.exec('DELETE FROM git_objects_hot WHERE sha = ?', sha);
|
|
298
|
-
// Delete from warm tier (R2)
|
|
299
|
-
const key = this.buildR2Key(sha);
|
|
300
|
-
await this.r2.delete(key);
|
|
301
|
-
// Delete metadata
|
|
302
|
-
this.sql.exec('DELETE FROM git_objects_meta WHERE sha = ?', sha);
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Manually promote an object to the hot tier.
|
|
306
|
-
*
|
|
307
|
-
* @param sha - Object SHA to promote
|
|
308
|
-
* @param type - Object type
|
|
309
|
-
* @param data - Object data
|
|
310
|
-
* @returns True if promotion succeeded
|
|
311
|
-
*/
|
|
312
|
-
async promoteToHot(sha, type, data) {
|
|
313
|
-
await this.initialize();
|
|
314
|
-
const size = data.length;
|
|
315
|
-
// Check if object fits in hot tier
|
|
316
|
-
if (size > this.hotTierMaxObjectSize) {
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
const currentBytes = await this.getHotTierBytes();
|
|
320
|
-
if (currentBytes + size > this.hotTierMaxBytes) {
|
|
321
|
-
// Need to evict objects to make room
|
|
322
|
-
const evicted = await this.evictFromHotTier(size);
|
|
323
|
-
if (!evicted) {
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// Store in hot tier
|
|
328
|
-
await this.storeInHotTier(sha, type, data);
|
|
329
|
-
// Update metadata
|
|
330
|
-
this.sql.exec('UPDATE git_objects_meta SET tier = ? WHERE sha = ?', 'hot', sha);
|
|
331
|
-
this.promotions++;
|
|
332
|
-
return true;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Demote an object from hot to warm tier.
|
|
336
|
-
*
|
|
337
|
-
* @param sha - Object SHA to demote
|
|
338
|
-
*/
|
|
339
|
-
async demoteToWarm(sha) {
|
|
340
|
-
await this.initialize();
|
|
341
|
-
// Get object from hot tier
|
|
342
|
-
const rows = this.sql.exec('SELECT type, data FROM git_objects_hot WHERE sha = ?', sha).toArray();
|
|
343
|
-
if (rows.length === 0)
|
|
344
|
-
return;
|
|
345
|
-
const { type, data } = rows[0];
|
|
346
|
-
// Store in warm tier
|
|
347
|
-
await this.storeInWarmTier(sha, type, data);
|
|
348
|
-
// Remove from hot tier
|
|
349
|
-
this.sql.exec('DELETE FROM git_objects_hot WHERE sha = ?', sha);
|
|
350
|
-
// Update metadata
|
|
351
|
-
this.sql.exec('UPDATE git_objects_meta SET tier = ? WHERE sha = ?', 'warm', sha);
|
|
352
|
-
this.demotions++;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Demote an object from warm to cold tier (packfile).
|
|
356
|
-
*
|
|
357
|
-
* @description
|
|
358
|
-
* This is typically done during packfile creation, where multiple
|
|
359
|
-
* warm objects are combined into a packfile for efficiency.
|
|
360
|
-
*
|
|
361
|
-
* @param sha - Object SHA to demote
|
|
362
|
-
* @param packId - Packfile ID where object will be stored
|
|
363
|
-
* @param packOffset - Byte offset within the packfile
|
|
364
|
-
*/
|
|
365
|
-
async demoteToCold(sha, packId, packOffset) {
|
|
366
|
-
await this.initialize();
|
|
367
|
-
// Delete from warm tier
|
|
368
|
-
const key = this.buildR2Key(sha);
|
|
369
|
-
await this.r2.delete(key);
|
|
370
|
-
// Update metadata with pack location
|
|
371
|
-
this.sql.exec('UPDATE git_objects_meta SET tier = ?, pack_id = ?, pack_offset = ? WHERE sha = ?', 'cold', packId, packOffset, sha);
|
|
372
|
-
this.demotions++;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Run maintenance tasks (demotion of old objects).
|
|
376
|
-
*
|
|
377
|
-
* @description
|
|
378
|
-
* This should be called periodically to:
|
|
379
|
-
* 1. Demote old hot tier objects to warm
|
|
380
|
-
* 2. Optionally pack warm objects into cold tier
|
|
381
|
-
*
|
|
382
|
-
* @param options - Maintenance options
|
|
383
|
-
* @returns Number of objects demoted
|
|
384
|
-
*/
|
|
385
|
-
async runMaintenance(options) {
|
|
386
|
-
await this.initialize();
|
|
387
|
-
if (!this.autoDemote)
|
|
388
|
-
return 0;
|
|
389
|
-
const cutoffTime = Date.now() - (this.demotionAgeDays * 24 * 60 * 60 * 1000);
|
|
390
|
-
let demoted = 0;
|
|
391
|
-
// Find hot tier objects that should be demoted
|
|
392
|
-
const rows = this.sql.exec(`SELECT sha FROM git_objects_meta
|
|
393
|
-
WHERE tier = 'hot' AND last_accessed < ?
|
|
394
|
-
ORDER BY last_accessed ASC
|
|
395
|
-
LIMIT 100`, cutoffTime).toArray();
|
|
396
|
-
for (const row of rows) {
|
|
397
|
-
if (!options?.dryRun) {
|
|
398
|
-
await this.demoteToWarm(row.sha);
|
|
399
|
-
}
|
|
400
|
-
demoted++;
|
|
401
|
-
}
|
|
402
|
-
return demoted;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Get storage statistics.
|
|
406
|
-
*/
|
|
407
|
-
async getStats() {
|
|
408
|
-
await this.initialize();
|
|
409
|
-
const hotCount = this.sql.exec("SELECT COUNT(*) as count FROM git_objects_meta WHERE tier = 'hot'").toArray()[0]?.count ?? 0;
|
|
410
|
-
const hotBytes = await this.getHotTierBytes();
|
|
411
|
-
const warmCount = this.sql.exec("SELECT COUNT(*) as count FROM git_objects_meta WHERE tier = 'warm'").toArray()[0]?.count ?? 0;
|
|
412
|
-
const coldCount = this.sql.exec("SELECT COUNT(*) as count FROM git_objects_meta WHERE tier = 'cold'").toArray()[0]?.count ?? 0;
|
|
413
|
-
const totalHits = this.hotHits + this.warmHits + this.coldHits;
|
|
414
|
-
const totalRequests = totalHits + this.misses;
|
|
415
|
-
const hitRate = totalRequests > 0 ? totalHits / totalRequests : 0;
|
|
416
|
-
return {
|
|
417
|
-
hotTierCount: hotCount,
|
|
418
|
-
hotTierBytes: hotBytes,
|
|
419
|
-
warmTierCount: warmCount,
|
|
420
|
-
coldTierCount: coldCount,
|
|
421
|
-
totalObjects: hotCount + warmCount + coldCount,
|
|
422
|
-
cacheHitRate: hitRate,
|
|
423
|
-
promotions: this.promotions,
|
|
424
|
-
demotions: this.demotions
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Create a packfile from warm tier objects.
|
|
429
|
-
*
|
|
430
|
-
* @description
|
|
431
|
-
* Combines multiple warm tier objects into a packfile stored in R2.
|
|
432
|
-
* This is more storage-efficient and can reduce costs.
|
|
433
|
-
*
|
|
434
|
-
* @param shas - Array of SHA hashes to pack
|
|
435
|
-
* @returns Pack ID and size
|
|
436
|
-
*/
|
|
437
|
-
async createPackfile(shas) {
|
|
438
|
-
await this.initialize();
|
|
439
|
-
// Generate pack ID
|
|
440
|
-
const packId = `pack-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
441
|
-
// Collect objects
|
|
442
|
-
const objects = [];
|
|
443
|
-
for (const sha of shas) {
|
|
444
|
-
const result = await this.getFromWarmTier(sha);
|
|
445
|
-
if (result) {
|
|
446
|
-
objects.push({ sha, ...result });
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (objects.length === 0) {
|
|
450
|
-
return { packId, size: 0, objectCount: 0 };
|
|
451
|
-
}
|
|
452
|
-
// Build simple packfile format
|
|
453
|
-
const packData = this.buildPackfile(objects);
|
|
454
|
-
// Store in R2
|
|
455
|
-
const packKey = `${this.prefix}/packs/${packId}.pack`;
|
|
456
|
-
await this.r2.put(packKey, packData, {
|
|
457
|
-
customMetadata: {
|
|
458
|
-
objectCount: String(objects.length),
|
|
459
|
-
createdAt: new Date().toISOString()
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
// Update metadata for each object
|
|
463
|
-
let offset = 12; // Pack header size
|
|
464
|
-
for (const obj of objects) {
|
|
465
|
-
await this.demoteToCold(obj.sha, packId, offset);
|
|
466
|
-
offset += obj.data.length + 10; // Rough estimate for object header
|
|
467
|
-
}
|
|
468
|
-
return {
|
|
469
|
-
packId,
|
|
470
|
-
size: packData.length,
|
|
471
|
-
objectCount: objects.length
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
// =========================================================================
|
|
475
|
-
// Private Helper Methods
|
|
476
|
-
// =========================================================================
|
|
477
|
-
/**
|
|
478
|
-
* Get object from hot tier (SQLite).
|
|
479
|
-
*/
|
|
480
|
-
async getFromHotTier(sha) {
|
|
481
|
-
const rows = this.sql.exec('SELECT type, data FROM git_objects_hot WHERE sha = ?', sha).toArray();
|
|
482
|
-
if (rows.length === 0)
|
|
483
|
-
return null;
|
|
484
|
-
return {
|
|
485
|
-
type: rows[0].type,
|
|
486
|
-
data: rows[0].data
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Get object from warm tier (R2 loose objects).
|
|
491
|
-
*/
|
|
492
|
-
async getFromWarmTier(sha) {
|
|
493
|
-
const key = this.buildR2Key(sha);
|
|
494
|
-
const obj = await this.r2.get(key);
|
|
495
|
-
if (!obj)
|
|
496
|
-
return null;
|
|
497
|
-
const data = new Uint8Array(await obj.arrayBuffer());
|
|
498
|
-
const type = (obj.customMetadata?.type ?? 'blob');
|
|
499
|
-
return { type, data };
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Get object from cold tier (R2 packfile).
|
|
503
|
-
*/
|
|
504
|
-
async getFromColdTier(packId, offset) {
|
|
505
|
-
const packKey = `${this.prefix}/packs/${packId}.pack`;
|
|
506
|
-
const packObj = await this.r2.get(packKey);
|
|
507
|
-
if (!packObj)
|
|
508
|
-
return null;
|
|
509
|
-
const packData = new Uint8Array(await packObj.arrayBuffer());
|
|
510
|
-
// Parse object from packfile at offset
|
|
511
|
-
const result = this.parsePackObject(packData, offset);
|
|
512
|
-
return result;
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* Store object in hot tier.
|
|
516
|
-
*/
|
|
517
|
-
async storeInHotTier(sha, type, data) {
|
|
518
|
-
const now = Date.now();
|
|
519
|
-
this.sql.exec(`INSERT OR REPLACE INTO git_objects_hot (sha, type, data, size, created_at)
|
|
520
|
-
VALUES (?, ?, ?, ?, ?)`, sha, type, data, data.length, now);
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* Store object in warm tier.
|
|
524
|
-
*/
|
|
525
|
-
async storeInWarmTier(sha, type, data) {
|
|
526
|
-
const key = this.buildR2Key(sha);
|
|
527
|
-
await this.r2.put(key, data, {
|
|
528
|
-
customMetadata: {
|
|
529
|
-
type,
|
|
530
|
-
size: String(data.length),
|
|
531
|
-
createdAt: new Date().toISOString()
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Promote object to warm tier.
|
|
537
|
-
*/
|
|
538
|
-
async promoteToWarm(sha, type, data) {
|
|
539
|
-
await this.storeInWarmTier(sha, type, data);
|
|
540
|
-
this.sql.exec('UPDATE git_objects_meta SET tier = ?, pack_id = NULL, pack_offset = NULL WHERE sha = ?', 'warm', sha);
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Get metadata for an object.
|
|
544
|
-
*/
|
|
545
|
-
async getMetadata(sha) {
|
|
546
|
-
const rows = this.sql.exec('SELECT * FROM git_objects_meta WHERE sha = ?', sha).toArray();
|
|
547
|
-
if (rows.length === 0)
|
|
548
|
-
return null;
|
|
549
|
-
const row = rows[0];
|
|
550
|
-
return {
|
|
551
|
-
sha: row.sha,
|
|
552
|
-
type: row.type,
|
|
553
|
-
size: row.size,
|
|
554
|
-
tier: row.tier,
|
|
555
|
-
accessCount: row.access_count,
|
|
556
|
-
lastAccessed: row.last_accessed,
|
|
557
|
-
createdAt: row.created_at,
|
|
558
|
-
packId: row.pack_id ?? undefined,
|
|
559
|
-
packOffset: row.pack_offset ?? undefined
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Record an access to an object.
|
|
564
|
-
*/
|
|
565
|
-
async recordAccess(sha) {
|
|
566
|
-
const now = Date.now();
|
|
567
|
-
this.sql.exec('UPDATE git_objects_meta SET access_count = access_count + 1, last_accessed = ? WHERE sha = ?', now, sha);
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* Get total bytes in hot tier.
|
|
571
|
-
*/
|
|
572
|
-
async getHotTierBytes() {
|
|
573
|
-
const rows = this.sql.exec('SELECT SUM(size) as total FROM git_objects_hot').toArray();
|
|
574
|
-
return rows[0]?.total ?? 0;
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Evict objects from hot tier to make room.
|
|
578
|
-
*/
|
|
579
|
-
async evictFromHotTier(neededBytes) {
|
|
580
|
-
void await this.getHotTierBytes(); // Track current usage
|
|
581
|
-
let freedBytes = 0;
|
|
582
|
-
const targetFree = neededBytes;
|
|
583
|
-
// Get LRU objects from hot tier
|
|
584
|
-
const rows = this.sql.exec(`SELECT m.sha, h.size
|
|
585
|
-
FROM git_objects_meta m
|
|
586
|
-
JOIN git_objects_hot h ON m.sha = h.sha
|
|
587
|
-
WHERE m.tier = 'hot'
|
|
588
|
-
ORDER BY m.last_accessed ASC
|
|
589
|
-
LIMIT 50`).toArray();
|
|
590
|
-
for (const row of rows) {
|
|
591
|
-
if (freedBytes >= targetFree)
|
|
592
|
-
break;
|
|
593
|
-
await this.demoteToWarm(row.sha);
|
|
594
|
-
freedBytes += row.size;
|
|
595
|
-
}
|
|
596
|
-
return freedBytes >= targetFree;
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* Build R2 key for a loose object.
|
|
600
|
-
*/
|
|
601
|
-
buildR2Key(sha) {
|
|
602
|
-
// Use git's standard 2-character prefix directory structure
|
|
603
|
-
return `${this.prefix}/${sha.slice(0, 2)}/${sha.slice(2)}`;
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Build a simple packfile from objects.
|
|
607
|
-
*/
|
|
608
|
-
buildPackfile(objects) {
|
|
609
|
-
// Pack header: "PACK" + version (2) + object count
|
|
610
|
-
const header = new Uint8Array(12);
|
|
611
|
-
header[0] = 0x50; // P
|
|
612
|
-
header[1] = 0x41; // A
|
|
613
|
-
header[2] = 0x43; // C
|
|
614
|
-
header[3] = 0x4B; // K
|
|
615
|
-
header[4] = 0x00;
|
|
616
|
-
header[5] = 0x00;
|
|
617
|
-
header[6] = 0x00;
|
|
618
|
-
header[7] = 0x02; // Version 2
|
|
619
|
-
// Object count (big endian)
|
|
620
|
-
const count = objects.length;
|
|
621
|
-
header[8] = (count >> 24) & 0xff;
|
|
622
|
-
header[9] = (count >> 16) & 0xff;
|
|
623
|
-
header[10] = (count >> 8) & 0xff;
|
|
624
|
-
header[11] = count & 0xff;
|
|
625
|
-
// Calculate total size
|
|
626
|
-
let totalSize = 12; // header
|
|
627
|
-
for (const obj of objects) {
|
|
628
|
-
totalSize += 1 + obj.data.length; // type byte + data
|
|
629
|
-
}
|
|
630
|
-
totalSize += 20; // trailing checksum
|
|
631
|
-
const pack = new Uint8Array(totalSize);
|
|
632
|
-
pack.set(header, 0);
|
|
633
|
-
let offset = 12;
|
|
634
|
-
const typeMap = {
|
|
635
|
-
commit: 1,
|
|
636
|
-
tree: 2,
|
|
637
|
-
blob: 3,
|
|
638
|
-
tag: 4
|
|
639
|
-
};
|
|
640
|
-
for (const obj of objects) {
|
|
641
|
-
// Simple encoding: type nibble + size
|
|
642
|
-
const typeNum = typeMap[obj.type] ?? 3;
|
|
643
|
-
pack[offset] = (typeNum << 4) | (obj.data.length & 0x0f);
|
|
644
|
-
offset++;
|
|
645
|
-
pack.set(obj.data, offset);
|
|
646
|
-
offset += obj.data.length;
|
|
647
|
-
}
|
|
648
|
-
// Trailing zeros for checksum (simplified)
|
|
649
|
-
return pack;
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* Parse an object from packfile data at offset.
|
|
653
|
-
*/
|
|
654
|
-
parsePackObject(packData, offset) {
|
|
655
|
-
if (offset >= packData.length)
|
|
656
|
-
return null;
|
|
657
|
-
// Read type and size from first byte
|
|
658
|
-
const firstByte = packData[offset];
|
|
659
|
-
const typeNum = (firstByte >> 4) & 0x07;
|
|
660
|
-
const typeMap = {
|
|
661
|
-
1: 'commit',
|
|
662
|
-
2: 'tree',
|
|
663
|
-
3: 'blob',
|
|
664
|
-
4: 'tag'
|
|
665
|
-
};
|
|
666
|
-
const type = typeMap[typeNum] ?? 'blob';
|
|
667
|
-
// For simplicity, read until next object or end
|
|
668
|
-
// In production, this would use proper variable-length encoding
|
|
669
|
-
let end = offset + 1;
|
|
670
|
-
while (end < packData.length && end < offset + 10000) {
|
|
671
|
-
end++;
|
|
672
|
-
}
|
|
673
|
-
const data = packData.slice(offset + 1, end);
|
|
674
|
-
return { type, data };
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
// ============================================================================
|
|
678
|
-
// Factory Functions
|
|
679
|
-
// ============================================================================
|
|
680
|
-
/**
|
|
681
|
-
* Create a TieredStorage instance.
|
|
682
|
-
*
|
|
683
|
-
* @param options - Configuration options
|
|
684
|
-
* @returns Configured TieredStorage instance
|
|
685
|
-
*/
|
|
686
|
-
export function createTieredStorage(options) {
|
|
687
|
-
return new TieredStorage(options);
|
|
688
|
-
}
|
|
689
|
-
//# sourceMappingURL=tiered-storage.js.map
|