@pierre/storage 0.0.1
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 +163 -0
- package/dist/index.cjs +110 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
- package/src/index.ts +178 -0
- package/src/types.ts +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @pierre/storage
|
|
2
|
+
|
|
3
|
+
Pierre Git Storage SDK for TypeScript/JavaScript applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pierre/storage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Basic Setup
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { GitStorage } from '@pierre/storage';
|
|
17
|
+
|
|
18
|
+
// Initialize the client with your name and key
|
|
19
|
+
const store = new GitStorage({
|
|
20
|
+
name: 'your-name', // e.g., 'v0'
|
|
21
|
+
key: 'your-key', // Your API key
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Creating a Repository
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// Create a new repository with auto-generated ID
|
|
29
|
+
const repo = await store.createRepo();
|
|
30
|
+
console.log(repo.id); // e.g., '123e4567-e89b-12d3-a456-426614174000'
|
|
31
|
+
|
|
32
|
+
// Create a repository with custom ID
|
|
33
|
+
const customRepo = await store.createRepo({ id: 'my-custom-repo' });
|
|
34
|
+
console.log(customRepo.id); // 'my-custom-repo'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Finding a Repository
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const foundRepo = await store.findOne({ id: 'repo-id' });
|
|
41
|
+
if (foundRepo) {
|
|
42
|
+
const url = await foundRepo.getRemoteURL();
|
|
43
|
+
console.log(`Repository URL: ${url}`);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Getting Remote URLs
|
|
48
|
+
|
|
49
|
+
The SDK generates secure URLs with JWT authentication for Git operations:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Get URL with default permissions (git:write, git:read) and 1-year TTL
|
|
53
|
+
const url = await repo.getRemoteURL();
|
|
54
|
+
// Returns: https://t:JWT@your-name.git.storage/repo-id.git
|
|
55
|
+
|
|
56
|
+
// Configure the Git remote
|
|
57
|
+
console.log(`Run: git remote add origin ${url}`);
|
|
58
|
+
|
|
59
|
+
// Get URL with custom permissions and TTL
|
|
60
|
+
const readOnlyUrl = await repo.getRemoteURL({
|
|
61
|
+
permissions: ['git:read'], // Read-only access
|
|
62
|
+
ttl: 3600, // 1 hour in seconds
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Available permissions:
|
|
66
|
+
// - 'git:read' - Read access to Git repository
|
|
67
|
+
// - 'git:write' - Write access to Git repository
|
|
68
|
+
// - 'repo:write' - Create a repository
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API Reference
|
|
72
|
+
|
|
73
|
+
### GitStorage
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
class GitStorage {
|
|
77
|
+
constructor(options: GitStorageOptions);
|
|
78
|
+
async createRepo(options?: CreateRepoOptions): Promise<Repo>;
|
|
79
|
+
async findOne(options: FindOneOptions): Promise<Repo | null>;
|
|
80
|
+
getConfig(): GitStorageOptions;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Interfaces
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface GitStorageOptions {
|
|
88
|
+
name: string; // Your identifier
|
|
89
|
+
key: string; // Your API key
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface CreateRepoOptions {
|
|
93
|
+
id?: string; // Optional custom repository ID
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface FindOneOptions {
|
|
97
|
+
id: string; // Repository ID to find
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface Repo {
|
|
101
|
+
id: string;
|
|
102
|
+
getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface GetRemoteURLOptions {
|
|
106
|
+
permissions?: ('git:write' | 'git:read' | 'repo:write')[];
|
|
107
|
+
ttl?: number; // Time to live in seconds (default: 31536000 = 1 year)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Authentication
|
|
112
|
+
|
|
113
|
+
The SDK uses JWT (JSON Web Tokens) for authentication. When you call `getRemoteURL()`, it:
|
|
114
|
+
|
|
115
|
+
1. Creates a JWT with your name, repository ID, and requested permissions
|
|
116
|
+
2. Signs it with your key
|
|
117
|
+
3. Embeds it in the Git remote URL as the password
|
|
118
|
+
|
|
119
|
+
The generated URLs are compatible with standard Git clients and include all necessary
|
|
120
|
+
authentication.
|
|
121
|
+
|
|
122
|
+
## Error Handling
|
|
123
|
+
|
|
124
|
+
The SDK validates inputs and provides helpful error messages:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
try {
|
|
128
|
+
const store = new GitStorage({ name: '', key: 'key' });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// Error: GitStorage name must be a non-empty string.
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const store = new GitStorage({ name: 'v0', key: '' });
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// Error: GitStorage key must be a non-empty string.
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
This package is part of the Pierre monorepo. To work on it locally:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Install dependencies
|
|
146
|
+
pnpm install
|
|
147
|
+
|
|
148
|
+
# Build the package
|
|
149
|
+
moon git-storage-sdk:build
|
|
150
|
+
|
|
151
|
+
# Run tests
|
|
152
|
+
moon git-storage-sdk:test
|
|
153
|
+
|
|
154
|
+
# Run in watch mode
|
|
155
|
+
moon git-storage-sdk:dev
|
|
156
|
+
|
|
157
|
+
# Format code
|
|
158
|
+
moon git-storage-sdk:format-write
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jose = require('jose');
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var API_BASE_URL = "https://api.git.storage";
|
|
7
|
+
var STORAGE_BASE_URL = "git.storage";
|
|
8
|
+
var GitStorage = class {
|
|
9
|
+
options;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
if (!options || options.name === void 0 || options.key === void 0 || options.name === null || options.key === null) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
"GitStorage requires a name and key. Please check your configuration and try again."
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
if (typeof options.name !== "string" || options.name.trim() === "") {
|
|
17
|
+
throw new Error("GitStorage name must be a non-empty string.");
|
|
18
|
+
}
|
|
19
|
+
if (typeof options.key !== "string" || options.key.trim() === "") {
|
|
20
|
+
throw new Error("GitStorage key must be a non-empty string.");
|
|
21
|
+
}
|
|
22
|
+
this.options = {
|
|
23
|
+
key: options.key,
|
|
24
|
+
name: options.name
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a new repository
|
|
29
|
+
* @returns The created repository
|
|
30
|
+
*/
|
|
31
|
+
async createRepo(options) {
|
|
32
|
+
const repoId = options?.id || crypto.randomUUID();
|
|
33
|
+
const jwt = await this.generateJWT(repoId, {
|
|
34
|
+
permissions: ["repo:write"],
|
|
35
|
+
ttl: 1 * 60 * 60
|
|
36
|
+
// 1hr in seconds
|
|
37
|
+
});
|
|
38
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/repos`, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${jwt}`
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`Failed to create repository: ${response.statusText}`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
id: repoId,
|
|
49
|
+
getRemoteURL: async (urlOptions) => {
|
|
50
|
+
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${repoId}.git`);
|
|
51
|
+
url.username = `t`;
|
|
52
|
+
url.password = await this.generateJWT(repoId, urlOptions);
|
|
53
|
+
return url.toString();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Find a repository by ID
|
|
59
|
+
* @param options The search options
|
|
60
|
+
* @returns The found repository
|
|
61
|
+
*/
|
|
62
|
+
async findOne(options) {
|
|
63
|
+
return {
|
|
64
|
+
id: options.id,
|
|
65
|
+
getRemoteURL: async (urlOptions) => {
|
|
66
|
+
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${options.id}.git`);
|
|
67
|
+
url.username = `t`;
|
|
68
|
+
url.password = await this.generateJWT(options.id, urlOptions);
|
|
69
|
+
return url.toString();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the current configuration
|
|
75
|
+
* @returns The client configuration
|
|
76
|
+
*/
|
|
77
|
+
getConfig() {
|
|
78
|
+
return { ...this.options };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Generate a JWT token for git storage URL authentication
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
async generateJWT(repoId, options) {
|
|
85
|
+
const permissions = options?.permissions || ["git:write", "git:read"];
|
|
86
|
+
const ttl = options?.ttl || 365 * 24 * 60 * 60;
|
|
87
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
88
|
+
const payload = {
|
|
89
|
+
aud: "git.pierre.co",
|
|
90
|
+
// todo this should be updated to git.storage
|
|
91
|
+
iss: this.options.name,
|
|
92
|
+
sub: "@pierre/storage",
|
|
93
|
+
repo: repoId,
|
|
94
|
+
scopes: permissions,
|
|
95
|
+
iat: now,
|
|
96
|
+
exp: now + ttl
|
|
97
|
+
};
|
|
98
|
+
const key = await jose.importPKCS8(this.options.key, "ES256");
|
|
99
|
+
const jwt = await new jose.SignJWT(payload).setProtectedHeader({ alg: "ES256", typ: "JWT" }).sign(key);
|
|
100
|
+
return jwt;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
function createClient(options) {
|
|
104
|
+
return new GitStorage(options);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
exports.GitStorage = GitStorage;
|
|
108
|
+
exports.createClient = createClient;
|
|
109
|
+
//# sourceMappingURL=index.cjs.map
|
|
110
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["importPKCS8","SignJWT"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Pierre Git Storage SDK\n *\n * A TypeScript SDK for interacting with Pierre's git storage system\n */\n\nimport { importPKCS8, SignJWT } from 'jose';\n\n/**\n * Type definitions for Pierre Git Storage SDK\n */\n\nexport interface GitStorageOptions {\n\tkey: string;\n\tname: string;\n}\n\nexport interface GetRemoteURLOptions {\n\tpermissions?: ('git:write' | 'git:read' | 'repo:write')[];\n\tttl?: number;\n}\n\nexport interface Repo {\n\tid: string;\n\tgetRemoteURL(options?: GetRemoteURLOptions): Promise<string>;\n}\n\nexport interface FindOneOptions {\n\tid: string;\n}\n\nexport interface CreateRepoOptions {\n\tid?: string;\n}\n\nexport interface CreateRepoResponse {\n\trepo_id: string;\n\turl: string;\n}\n\n/**\n * Git Storage API\n */\ndeclare const __API_BASE_URL__: string;\ndeclare const __STORAGE_BASE_URL__: string;\n\nconst API_BASE_URL = __API_BASE_URL__;\nconst STORAGE_BASE_URL = __STORAGE_BASE_URL__;\n\nexport class GitStorage {\n\tprivate options: GitStorageOptions;\n\n\tconstructor(options: GitStorageOptions) {\n\t\tif (\n\t\t\t!options ||\n\t\t\toptions.name === undefined ||\n\t\t\toptions.key === undefined ||\n\t\t\toptions.name === null ||\n\t\t\toptions.key === null\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t'GitStorage requires a name and key. Please check your configuration and try again.',\n\t\t\t);\n\t\t}\n\n\t\tif (typeof options.name !== 'string' || options.name.trim() === '') {\n\t\t\tthrow new Error('GitStorage name must be a non-empty string.');\n\t\t}\n\n\t\tif (typeof options.key !== 'string' || options.key.trim() === '') {\n\t\t\tthrow new Error('GitStorage key must be a non-empty string.');\n\t\t}\n\n\t\tthis.options = {\n\t\t\tkey: options.key,\n\t\t\tname: options.name,\n\t\t};\n\t}\n\n\t/**\n\t * Create a new repository\n\t * @returns The created repository\n\t */\n\tasync createRepo(options?: CreateRepoOptions): Promise<Repo> {\n\t\tconst repoId = options?.id || crypto.randomUUID();\n\n\t\tconst jwt = await this.generateJWT(repoId, {\n\t\t\tpermissions: ['repo:write'],\n\t\t\tttl: 1 * 60 * 60, // 1hr in seconds\n\t\t});\n\n\t\tconst response = await fetch(`${API_BASE_URL}/api/v1/repos`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${jwt}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to create repository: ${response.statusText}`);\n\t\t}\n\n\t\treturn {\n\t\t\tid: repoId,\n\t\t\tgetRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {\n\t\t\t\tconst url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${repoId}.git`);\n\t\t\t\turl.username = `t`;\n\t\t\t\turl.password = await this.generateJWT(repoId, urlOptions);\n\t\t\t\treturn url.toString();\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Find a repository by ID\n\t * @param options The search options\n\t * @returns The found repository\n\t */\n\tasync findOne(options: FindOneOptions): Promise<Repo | null> {\n\t\treturn {\n\t\t\tid: options.id,\n\t\t\tgetRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {\n\t\t\t\tconst url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${options.id}.git`);\n\t\t\t\turl.username = `t`;\n\t\t\t\turl.password = await this.generateJWT(options.id, urlOptions);\n\t\t\t\treturn url.toString();\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Get the current configuration\n\t * @returns The client configuration\n\t */\n\tgetConfig(): GitStorageOptions {\n\t\treturn { ...this.options };\n\t}\n\n\t/**\n\t * Generate a JWT token for git storage URL authentication\n\t * @private\n\t */\n\tprivate async generateJWT(repoId: string, options?: GetRemoteURLOptions): Promise<string> {\n\t\t// Default permissions and TTL\n\t\tconst permissions = options?.permissions || ['git:write', 'git:read'];\n\t\tconst ttl = options?.ttl || 365 * 24 * 60 * 60; // 1 year in seconds\n\n\t\t// Create the JWT payload\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst payload = {\n\t\t\taud: 'git.pierre.co', // todo this should be updated to git.storage\n\t\t\tiss: this.options.name,\n\t\t\tsub: '@pierre/storage',\n\t\t\trepo: repoId,\n\t\t\tscopes: permissions,\n\t\t\tiat: now,\n\t\t\texp: now + ttl,\n\t\t};\n\n\t\t// Sign the JWT with the key as the secret\n\t\t// Using HS256 for symmetric signing with the key\n\t\tconst key = await importPKCS8(this.options.key, 'ES256');\n\t\t// Sign the JWT with the key as the secret\n\t\tconst jwt = await new SignJWT(payload)\n\t\t\t.setProtectedHeader({ alg: 'ES256', typ: 'JWT' })\n\t\t\t.sign(key);\n\n\t\treturn jwt;\n\t}\n}\n\n// Export a default client factory\nexport function createClient(options: GitStorageOptions): GitStorage {\n\treturn new GitStorage(options);\n}\n\n// Type alias for backward compatibility\nexport type StorageOptions = GitStorageOptions;\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pierre Git Storage SDK
|
|
3
|
+
*
|
|
4
|
+
* A TypeScript SDK for interacting with Pierre's git storage system
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Type definitions for Pierre Git Storage SDK
|
|
8
|
+
*/
|
|
9
|
+
interface GitStorageOptions {
|
|
10
|
+
key: string;
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
13
|
+
interface GetRemoteURLOptions {
|
|
14
|
+
permissions?: ('git:write' | 'git:read' | 'repo:write')[];
|
|
15
|
+
ttl?: number;
|
|
16
|
+
}
|
|
17
|
+
interface Repo {
|
|
18
|
+
id: string;
|
|
19
|
+
getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
interface FindOneOptions {
|
|
22
|
+
id: string;
|
|
23
|
+
}
|
|
24
|
+
interface CreateRepoOptions {
|
|
25
|
+
id?: string;
|
|
26
|
+
}
|
|
27
|
+
interface CreateRepoResponse {
|
|
28
|
+
repo_id: string;
|
|
29
|
+
url: string;
|
|
30
|
+
}
|
|
31
|
+
declare class GitStorage {
|
|
32
|
+
private options;
|
|
33
|
+
constructor(options: GitStorageOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Create a new repository
|
|
36
|
+
* @returns The created repository
|
|
37
|
+
*/
|
|
38
|
+
createRepo(options?: CreateRepoOptions): Promise<Repo>;
|
|
39
|
+
/**
|
|
40
|
+
* Find a repository by ID
|
|
41
|
+
* @param options The search options
|
|
42
|
+
* @returns The found repository
|
|
43
|
+
*/
|
|
44
|
+
findOne(options: FindOneOptions): Promise<Repo | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Get the current configuration
|
|
47
|
+
* @returns The client configuration
|
|
48
|
+
*/
|
|
49
|
+
getConfig(): GitStorageOptions;
|
|
50
|
+
/**
|
|
51
|
+
* Generate a JWT token for git storage URL authentication
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
private generateJWT;
|
|
55
|
+
}
|
|
56
|
+
declare function createClient(options: GitStorageOptions): GitStorage;
|
|
57
|
+
type StorageOptions = GitStorageOptions;
|
|
58
|
+
|
|
59
|
+
export { type CreateRepoOptions, type CreateRepoResponse, type FindOneOptions, type GetRemoteURLOptions, GitStorage, type GitStorageOptions, type Repo, type StorageOptions, createClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pierre Git Storage SDK
|
|
3
|
+
*
|
|
4
|
+
* A TypeScript SDK for interacting with Pierre's git storage system
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Type definitions for Pierre Git Storage SDK
|
|
8
|
+
*/
|
|
9
|
+
interface GitStorageOptions {
|
|
10
|
+
key: string;
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
13
|
+
interface GetRemoteURLOptions {
|
|
14
|
+
permissions?: ('git:write' | 'git:read' | 'repo:write')[];
|
|
15
|
+
ttl?: number;
|
|
16
|
+
}
|
|
17
|
+
interface Repo {
|
|
18
|
+
id: string;
|
|
19
|
+
getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
interface FindOneOptions {
|
|
22
|
+
id: string;
|
|
23
|
+
}
|
|
24
|
+
interface CreateRepoOptions {
|
|
25
|
+
id?: string;
|
|
26
|
+
}
|
|
27
|
+
interface CreateRepoResponse {
|
|
28
|
+
repo_id: string;
|
|
29
|
+
url: string;
|
|
30
|
+
}
|
|
31
|
+
declare class GitStorage {
|
|
32
|
+
private options;
|
|
33
|
+
constructor(options: GitStorageOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Create a new repository
|
|
36
|
+
* @returns The created repository
|
|
37
|
+
*/
|
|
38
|
+
createRepo(options?: CreateRepoOptions): Promise<Repo>;
|
|
39
|
+
/**
|
|
40
|
+
* Find a repository by ID
|
|
41
|
+
* @param options The search options
|
|
42
|
+
* @returns The found repository
|
|
43
|
+
*/
|
|
44
|
+
findOne(options: FindOneOptions): Promise<Repo | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Get the current configuration
|
|
47
|
+
* @returns The client configuration
|
|
48
|
+
*/
|
|
49
|
+
getConfig(): GitStorageOptions;
|
|
50
|
+
/**
|
|
51
|
+
* Generate a JWT token for git storage URL authentication
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
private generateJWT;
|
|
55
|
+
}
|
|
56
|
+
declare function createClient(options: GitStorageOptions): GitStorage;
|
|
57
|
+
type StorageOptions = GitStorageOptions;
|
|
58
|
+
|
|
59
|
+
export { type CreateRepoOptions, type CreateRepoResponse, type FindOneOptions, type GetRemoteURLOptions, GitStorage, type GitStorageOptions, type Repo, type StorageOptions, createClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { importPKCS8, SignJWT } from 'jose';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
var API_BASE_URL = "https://api.git.storage";
|
|
5
|
+
var STORAGE_BASE_URL = "git.storage";
|
|
6
|
+
var GitStorage = class {
|
|
7
|
+
options;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
if (!options || options.name === void 0 || options.key === void 0 || options.name === null || options.key === null) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"GitStorage requires a name and key. Please check your configuration and try again."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
if (typeof options.name !== "string" || options.name.trim() === "") {
|
|
15
|
+
throw new Error("GitStorage name must be a non-empty string.");
|
|
16
|
+
}
|
|
17
|
+
if (typeof options.key !== "string" || options.key.trim() === "") {
|
|
18
|
+
throw new Error("GitStorage key must be a non-empty string.");
|
|
19
|
+
}
|
|
20
|
+
this.options = {
|
|
21
|
+
key: options.key,
|
|
22
|
+
name: options.name
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a new repository
|
|
27
|
+
* @returns The created repository
|
|
28
|
+
*/
|
|
29
|
+
async createRepo(options) {
|
|
30
|
+
const repoId = options?.id || crypto.randomUUID();
|
|
31
|
+
const jwt = await this.generateJWT(repoId, {
|
|
32
|
+
permissions: ["repo:write"],
|
|
33
|
+
ttl: 1 * 60 * 60
|
|
34
|
+
// 1hr in seconds
|
|
35
|
+
});
|
|
36
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/repos`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: {
|
|
39
|
+
Authorization: `Bearer ${jwt}`
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`Failed to create repository: ${response.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
id: repoId,
|
|
47
|
+
getRemoteURL: async (urlOptions) => {
|
|
48
|
+
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${repoId}.git`);
|
|
49
|
+
url.username = `t`;
|
|
50
|
+
url.password = await this.generateJWT(repoId, urlOptions);
|
|
51
|
+
return url.toString();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find a repository by ID
|
|
57
|
+
* @param options The search options
|
|
58
|
+
* @returns The found repository
|
|
59
|
+
*/
|
|
60
|
+
async findOne(options) {
|
|
61
|
+
return {
|
|
62
|
+
id: options.id,
|
|
63
|
+
getRemoteURL: async (urlOptions) => {
|
|
64
|
+
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${options.id}.git`);
|
|
65
|
+
url.username = `t`;
|
|
66
|
+
url.password = await this.generateJWT(options.id, urlOptions);
|
|
67
|
+
return url.toString();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the current configuration
|
|
73
|
+
* @returns The client configuration
|
|
74
|
+
*/
|
|
75
|
+
getConfig() {
|
|
76
|
+
return { ...this.options };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate a JWT token for git storage URL authentication
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
async generateJWT(repoId, options) {
|
|
83
|
+
const permissions = options?.permissions || ["git:write", "git:read"];
|
|
84
|
+
const ttl = options?.ttl || 365 * 24 * 60 * 60;
|
|
85
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
86
|
+
const payload = {
|
|
87
|
+
aud: "git.pierre.co",
|
|
88
|
+
// todo this should be updated to git.storage
|
|
89
|
+
iss: this.options.name,
|
|
90
|
+
sub: "@pierre/storage",
|
|
91
|
+
repo: repoId,
|
|
92
|
+
scopes: permissions,
|
|
93
|
+
iat: now,
|
|
94
|
+
exp: now + ttl
|
|
95
|
+
};
|
|
96
|
+
const key = await importPKCS8(this.options.key, "ES256");
|
|
97
|
+
const jwt = await new SignJWT(payload).setProtectedHeader({ alg: "ES256", typ: "JWT" }).sign(key);
|
|
98
|
+
return jwt;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function createClient(options) {
|
|
102
|
+
return new GitStorage(options);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { GitStorage, createClient };
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Pierre Git Storage SDK\n *\n * A TypeScript SDK for interacting with Pierre's git storage system\n */\n\nimport { importPKCS8, SignJWT } from 'jose';\n\n/**\n * Type definitions for Pierre Git Storage SDK\n */\n\nexport interface GitStorageOptions {\n\tkey: string;\n\tname: string;\n}\n\nexport interface GetRemoteURLOptions {\n\tpermissions?: ('git:write' | 'git:read' | 'repo:write')[];\n\tttl?: number;\n}\n\nexport interface Repo {\n\tid: string;\n\tgetRemoteURL(options?: GetRemoteURLOptions): Promise<string>;\n}\n\nexport interface FindOneOptions {\n\tid: string;\n}\n\nexport interface CreateRepoOptions {\n\tid?: string;\n}\n\nexport interface CreateRepoResponse {\n\trepo_id: string;\n\turl: string;\n}\n\n/**\n * Git Storage API\n */\ndeclare const __API_BASE_URL__: string;\ndeclare const __STORAGE_BASE_URL__: string;\n\nconst API_BASE_URL = __API_BASE_URL__;\nconst STORAGE_BASE_URL = __STORAGE_BASE_URL__;\n\nexport class GitStorage {\n\tprivate options: GitStorageOptions;\n\n\tconstructor(options: GitStorageOptions) {\n\t\tif (\n\t\t\t!options ||\n\t\t\toptions.name === undefined ||\n\t\t\toptions.key === undefined ||\n\t\t\toptions.name === null ||\n\t\t\toptions.key === null\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t'GitStorage requires a name and key. Please check your configuration and try again.',\n\t\t\t);\n\t\t}\n\n\t\tif (typeof options.name !== 'string' || options.name.trim() === '') {\n\t\t\tthrow new Error('GitStorage name must be a non-empty string.');\n\t\t}\n\n\t\tif (typeof options.key !== 'string' || options.key.trim() === '') {\n\t\t\tthrow new Error('GitStorage key must be a non-empty string.');\n\t\t}\n\n\t\tthis.options = {\n\t\t\tkey: options.key,\n\t\t\tname: options.name,\n\t\t};\n\t}\n\n\t/**\n\t * Create a new repository\n\t * @returns The created repository\n\t */\n\tasync createRepo(options?: CreateRepoOptions): Promise<Repo> {\n\t\tconst repoId = options?.id || crypto.randomUUID();\n\n\t\tconst jwt = await this.generateJWT(repoId, {\n\t\t\tpermissions: ['repo:write'],\n\t\t\tttl: 1 * 60 * 60, // 1hr in seconds\n\t\t});\n\n\t\tconst response = await fetch(`${API_BASE_URL}/api/v1/repos`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${jwt}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to create repository: ${response.statusText}`);\n\t\t}\n\n\t\treturn {\n\t\t\tid: repoId,\n\t\t\tgetRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {\n\t\t\t\tconst url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${repoId}.git`);\n\t\t\t\turl.username = `t`;\n\t\t\t\turl.password = await this.generateJWT(repoId, urlOptions);\n\t\t\t\treturn url.toString();\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Find a repository by ID\n\t * @param options The search options\n\t * @returns The found repository\n\t */\n\tasync findOne(options: FindOneOptions): Promise<Repo | null> {\n\t\treturn {\n\t\t\tid: options.id,\n\t\t\tgetRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {\n\t\t\t\tconst url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${options.id}.git`);\n\t\t\t\turl.username = `t`;\n\t\t\t\turl.password = await this.generateJWT(options.id, urlOptions);\n\t\t\t\treturn url.toString();\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Get the current configuration\n\t * @returns The client configuration\n\t */\n\tgetConfig(): GitStorageOptions {\n\t\treturn { ...this.options };\n\t}\n\n\t/**\n\t * Generate a JWT token for git storage URL authentication\n\t * @private\n\t */\n\tprivate async generateJWT(repoId: string, options?: GetRemoteURLOptions): Promise<string> {\n\t\t// Default permissions and TTL\n\t\tconst permissions = options?.permissions || ['git:write', 'git:read'];\n\t\tconst ttl = options?.ttl || 365 * 24 * 60 * 60; // 1 year in seconds\n\n\t\t// Create the JWT payload\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst payload = {\n\t\t\taud: 'git.pierre.co', // todo this should be updated to git.storage\n\t\t\tiss: this.options.name,\n\t\t\tsub: '@pierre/storage',\n\t\t\trepo: repoId,\n\t\t\tscopes: permissions,\n\t\t\tiat: now,\n\t\t\texp: now + ttl,\n\t\t};\n\n\t\t// Sign the JWT with the key as the secret\n\t\t// Using HS256 for symmetric signing with the key\n\t\tconst key = await importPKCS8(this.options.key, 'ES256');\n\t\t// Sign the JWT with the key as the secret\n\t\tconst jwt = await new SignJWT(payload)\n\t\t\t.setProtectedHeader({ alg: 'ES256', typ: 'JWT' })\n\t\t\t.sign(key);\n\n\t\treturn jwt;\n\t}\n}\n\n// Export a default client factory\nexport function createClient(options: GitStorageOptions): GitStorage {\n\treturn new GitStorage(options);\n}\n\n// Type alias for backward compatibility\nexport type StorageOptions = GitStorageOptions;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pierre/storage",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Pierre Git Storage SDK",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"prepublishOnly": "BUILD_ENV=production pnpm build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"jose": "catalog:"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "catalog:",
|
|
32
|
+
"typescript": "catalog:",
|
|
33
|
+
"vitest": "catalog:"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pierre Git Storage SDK
|
|
3
|
+
*
|
|
4
|
+
* A TypeScript SDK for interacting with Pierre's git storage system
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { importPKCS8, SignJWT } from 'jose';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Type definitions for Pierre Git Storage SDK
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface GitStorageOptions {
|
|
14
|
+
key: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface GetRemoteURLOptions {
|
|
19
|
+
permissions?: ('git:write' | 'git:read' | 'repo:write')[];
|
|
20
|
+
ttl?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Repo {
|
|
24
|
+
id: string;
|
|
25
|
+
getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FindOneOptions {
|
|
29
|
+
id: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CreateRepoOptions {
|
|
33
|
+
id?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CreateRepoResponse {
|
|
37
|
+
repo_id: string;
|
|
38
|
+
url: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Git Storage API
|
|
43
|
+
*/
|
|
44
|
+
declare const __API_BASE_URL__: string;
|
|
45
|
+
declare const __STORAGE_BASE_URL__: string;
|
|
46
|
+
|
|
47
|
+
const API_BASE_URL = __API_BASE_URL__;
|
|
48
|
+
const STORAGE_BASE_URL = __STORAGE_BASE_URL__;
|
|
49
|
+
|
|
50
|
+
export class GitStorage {
|
|
51
|
+
private options: GitStorageOptions;
|
|
52
|
+
|
|
53
|
+
constructor(options: GitStorageOptions) {
|
|
54
|
+
if (
|
|
55
|
+
!options ||
|
|
56
|
+
options.name === undefined ||
|
|
57
|
+
options.key === undefined ||
|
|
58
|
+
options.name === null ||
|
|
59
|
+
options.key === null
|
|
60
|
+
) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
'GitStorage requires a name and key. Please check your configuration and try again.',
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof options.name !== 'string' || options.name.trim() === '') {
|
|
67
|
+
throw new Error('GitStorage name must be a non-empty string.');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof options.key !== 'string' || options.key.trim() === '') {
|
|
71
|
+
throw new Error('GitStorage key must be a non-empty string.');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.options = {
|
|
75
|
+
key: options.key,
|
|
76
|
+
name: options.name,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create a new repository
|
|
82
|
+
* @returns The created repository
|
|
83
|
+
*/
|
|
84
|
+
async createRepo(options?: CreateRepoOptions): Promise<Repo> {
|
|
85
|
+
const repoId = options?.id || crypto.randomUUID();
|
|
86
|
+
|
|
87
|
+
const jwt = await this.generateJWT(repoId, {
|
|
88
|
+
permissions: ['repo:write'],
|
|
89
|
+
ttl: 1 * 60 * 60, // 1hr in seconds
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/repos`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${jwt}`,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
throw new Error(`Failed to create repository: ${response.statusText}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
id: repoId,
|
|
105
|
+
getRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {
|
|
106
|
+
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${repoId}.git`);
|
|
107
|
+
url.username = `t`;
|
|
108
|
+
url.password = await this.generateJWT(repoId, urlOptions);
|
|
109
|
+
return url.toString();
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Find a repository by ID
|
|
116
|
+
* @param options The search options
|
|
117
|
+
* @returns The found repository
|
|
118
|
+
*/
|
|
119
|
+
async findOne(options: FindOneOptions): Promise<Repo | null> {
|
|
120
|
+
return {
|
|
121
|
+
id: options.id,
|
|
122
|
+
getRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {
|
|
123
|
+
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${options.id}.git`);
|
|
124
|
+
url.username = `t`;
|
|
125
|
+
url.password = await this.generateJWT(options.id, urlOptions);
|
|
126
|
+
return url.toString();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get the current configuration
|
|
133
|
+
* @returns The client configuration
|
|
134
|
+
*/
|
|
135
|
+
getConfig(): GitStorageOptions {
|
|
136
|
+
return { ...this.options };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate a JWT token for git storage URL authentication
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
private async generateJWT(repoId: string, options?: GetRemoteURLOptions): Promise<string> {
|
|
144
|
+
// Default permissions and TTL
|
|
145
|
+
const permissions = options?.permissions || ['git:write', 'git:read'];
|
|
146
|
+
const ttl = options?.ttl || 365 * 24 * 60 * 60; // 1 year in seconds
|
|
147
|
+
|
|
148
|
+
// Create the JWT payload
|
|
149
|
+
const now = Math.floor(Date.now() / 1000);
|
|
150
|
+
const payload = {
|
|
151
|
+
aud: 'git.pierre.co', // todo this should be updated to git.storage
|
|
152
|
+
iss: this.options.name,
|
|
153
|
+
sub: '@pierre/storage',
|
|
154
|
+
repo: repoId,
|
|
155
|
+
scopes: permissions,
|
|
156
|
+
iat: now,
|
|
157
|
+
exp: now + ttl,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Sign the JWT with the key as the secret
|
|
161
|
+
// Using HS256 for symmetric signing with the key
|
|
162
|
+
const key = await importPKCS8(this.options.key, 'ES256');
|
|
163
|
+
// Sign the JWT with the key as the secret
|
|
164
|
+
const jwt = await new SignJWT(payload)
|
|
165
|
+
.setProtectedHeader({ alg: 'ES256', typ: 'JWT' })
|
|
166
|
+
.sign(key);
|
|
167
|
+
|
|
168
|
+
return jwt;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Export a default client factory
|
|
173
|
+
export function createClient(options: GitStorageOptions): GitStorage {
|
|
174
|
+
return new GitStorage(options);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Type alias for backward compatibility
|
|
178
|
+
export type StorageOptions = GitStorageOptions;
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Pierre Git Storage SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface GitStorageOptions {
|
|
6
|
+
key: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface GetRemoteURLOptions {
|
|
11
|
+
permissions?: ('git:write' | 'git:read' | 'repo:write')[];
|
|
12
|
+
ttl?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Repo {
|
|
16
|
+
id: string;
|
|
17
|
+
getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FindOneOptions {
|
|
21
|
+
id: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CreateRepoOptions {
|
|
25
|
+
id?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CreateRepoResponse {
|
|
29
|
+
repo_id: string;
|
|
30
|
+
url: string;
|
|
31
|
+
}
|