@pierre/storage 1.2.1 → 1.3.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/package.json CHANGED
@@ -1,45 +1,45 @@
1
1
  {
2
- "name": "@pierre/storage",
3
- "version": "1.2.1",
4
- "description": "Pierre Git Storage SDK",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/pierrecomputer/sdk"
8
- },
9
- "license": "MIT",
10
- "type": "module",
11
- "main": "./dist/index.cjs",
12
- "module": "./dist/index.js",
13
- "types": "./dist/index.d.ts",
14
- "exports": {
15
- ".": {
16
- "types": "./dist/index.d.ts",
17
- "import": "./dist/index.js",
18
- "require": "./dist/index.cjs",
19
- "default": "./dist/index.js"
20
- }
21
- },
22
- "files": [
23
- "dist",
24
- "src"
25
- ],
26
- "scripts": {
27
- "build": "tsup",
28
- "dev": "tsup --watch",
29
- "prepublishOnly": "pnpm build"
30
- },
31
- "dependencies": {
32
- "jose": "^5.10.0",
33
- "snakecase-keys": "^9.0.2",
34
- "zod": "^3.23.8"
35
- },
36
- "devDependencies": {
37
- "@types/node": "^22.0.0",
38
- "tsup": "8.5.0",
39
- "typescript": "5.8.3",
40
- "vitest": "3.2.4"
41
- },
42
- "publishConfig": {
43
- "access": "public"
44
- }
2
+ "name": "@pierre/storage",
3
+ "version": "1.3.2",
4
+ "description": "Pierre Git Storage SDK",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/pierrecomputer/sdk"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "require": "./dist/index.cjs",
19
+ "default": "./dist/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "dev": "tsup --watch",
29
+ "prepublishOnly": "pnpm build"
30
+ },
31
+ "dependencies": {
32
+ "jose": "^5.10.0",
33
+ "snakecase-keys": "^9.0.2",
34
+ "zod": "^3.23.8"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "tsup": "8.5.0",
39
+ "typescript": "5.8.3",
40
+ "vitest": "3.2.4"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
45
  }
package/src/index.ts CHANGED
@@ -44,8 +44,10 @@ import type {
44
44
  CreateBranchResult,
45
45
  CreateCommitFromDiffOptions,
46
46
  CreateCommitOptions,
47
+ CreateGitCredentialOptions,
47
48
  CreateNoteOptions,
48
49
  CreateRepoOptions,
50
+ DeleteGitCredentialOptions,
49
51
  DeleteNoteOptions,
50
52
  DeleteRepoOptions,
51
53
  DeleteRepoResult,
@@ -54,6 +56,7 @@ import type {
54
56
  FileDiff,
55
57
  FilteredFile,
56
58
  FindOneOptions,
59
+ GenericGitBaseRepo,
57
60
  GetBranchDiffOptions,
58
61
  GetBranchDiffResponse,
59
62
  GetBranchDiffResult,
@@ -64,6 +67,8 @@ import type {
64
67
  GetNoteOptions,
65
68
  GetNoteResult,
66
69
  GetRemoteURLOptions,
70
+ GitCredential,
71
+ GitHubBaseRepo,
67
72
  GitStorageOptions,
68
73
  GrepFileMatch,
69
74
  GrepLine,
@@ -96,6 +101,7 @@ import type {
96
101
  Repo,
97
102
  RestoreCommitOptions,
98
103
  RestoreCommitResult,
104
+ UpdateGitCredentialOptions,
99
105
  ValidAPIVersion,
100
106
  } from './types';
101
107
 
@@ -635,6 +641,15 @@ class RepoImpl implements Repo {
635
641
  return url.toString();
636
642
  }
637
643
 
644
+ async getImportRemoteURL(urlOptions?: GetRemoteURLOptions): Promise<string> {
645
+ const url = new URL(
646
+ `https://${this.options.storageBaseUrl}/${this.id}+import.git`
647
+ );
648
+ url.username = `t`;
649
+ url.password = await this.generateJWT(this.id, urlOptions);
650
+ return url.toString();
651
+ }
652
+
638
653
  async getFileStream(options: GetFileOptions): Promise<Response> {
639
654
  const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
640
655
  const jwt = await this.generateJWT(this.id, {
@@ -1428,11 +1443,16 @@ export class GitStorage {
1428
1443
  ...(baseRepo.sha ? { sha: baseRepo.sha } : {}),
1429
1444
  };
1430
1445
  } else {
1446
+ // Sync base repo: GitHub or generic git provider (gitlab, bitbucket, etc.)
1447
+ const syncRepo = baseRepo as GitHubBaseRepo | GenericGitBaseRepo;
1448
+ const { provider: _p, ...restSnakecased } = snakecaseKeys(
1449
+ baseRepo as unknown as Record<string, unknown>
1450
+ ) as Record<string, unknown>;
1431
1451
  baseRepoOptions = {
1432
- provider: 'github',
1433
- ...snakecaseKeys(baseRepo as unknown as Record<string, unknown>),
1452
+ provider: syncRepo.provider ?? 'github',
1453
+ ...restSnakecased,
1434
1454
  };
1435
- resolvedDefaultBranch = baseRepo.defaultBranch;
1455
+ resolvedDefaultBranch = syncRepo.defaultBranch;
1436
1456
  }
1437
1457
  }
1438
1458
 
@@ -1581,6 +1601,96 @@ export class GitStorage {
1581
1601
  };
1582
1602
  }
1583
1603
 
1604
+ /**
1605
+ * Create a generic git credential for a repository.
1606
+ * Used to authenticate sync operations for non-GitHub providers (GitLab, Bitbucket, etc.)
1607
+ */
1608
+ async createGitCredential(
1609
+ options: CreateGitCredentialOptions
1610
+ ): Promise<GitCredential> {
1611
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
1612
+ const jwt = await this.generateJWT(options.repoId, {
1613
+ permissions: ['repo:write'],
1614
+ ttl,
1615
+ });
1616
+
1617
+ const body: Record<string, unknown> = {
1618
+ repo_id: options.repoId,
1619
+ password: options.password,
1620
+ };
1621
+ if (options.username !== undefined) {
1622
+ body.username = options.username;
1623
+ }
1624
+
1625
+ const resp = await this.api.post(
1626
+ { path: 'repos/git-credentials', body },
1627
+ jwt,
1628
+ { allowedStatus: [409] }
1629
+ );
1630
+ if (resp.status === 409) {
1631
+ throw new Error('A credential already exists for this repository');
1632
+ }
1633
+
1634
+ const data = (await resp.json()) as { id: string };
1635
+ return { id: data.id };
1636
+ }
1637
+
1638
+ /**
1639
+ * Update an existing generic git credential.
1640
+ */
1641
+ async updateGitCredential(
1642
+ options: UpdateGitCredentialOptions
1643
+ ): Promise<GitCredential> {
1644
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
1645
+ const jwt = await this.generateJWT('org', {
1646
+ permissions: ['repo:write'],
1647
+ ttl,
1648
+ });
1649
+
1650
+ const body: Record<string, unknown> = {
1651
+ id: options.id,
1652
+ password: options.password,
1653
+ };
1654
+ if (options.username !== undefined) {
1655
+ body.username = options.username;
1656
+ }
1657
+
1658
+ const resp = await this.api.put(
1659
+ { path: 'repos/git-credentials', body },
1660
+ jwt,
1661
+ { allowedStatus: [404] }
1662
+ );
1663
+ if (resp.status === 404) {
1664
+ throw new Error('Credential not found');
1665
+ }
1666
+
1667
+ const data = (await resp.json()) as { id: string; created_at?: string };
1668
+ return {
1669
+ id: data.id,
1670
+ ...(data.created_at ? { createdAt: data.created_at } : {}),
1671
+ };
1672
+ }
1673
+
1674
+ /**
1675
+ * Delete a generic git credential.
1676
+ */
1677
+ async deleteGitCredential(options: DeleteGitCredentialOptions): Promise<void> {
1678
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
1679
+ const jwt = await this.generateJWT('org', {
1680
+ permissions: ['repo:write'],
1681
+ ttl,
1682
+ });
1683
+
1684
+ const resp = await this.api.delete(
1685
+ { path: 'repos/git-credentials', body: { id: options.id } },
1686
+ jwt,
1687
+ { allowedStatus: [404] }
1688
+ );
1689
+ if (resp.status === 404) {
1690
+ throw new Error('Credential not found');
1691
+ }
1692
+ }
1693
+
1584
1694
  /**
1585
1695
  * Get the current configuration
1586
1696
  * @returns The client configuration
package/src/types.ts CHANGED
@@ -48,6 +48,7 @@ export interface Repo {
48
48
  createdAt: string;
49
49
  getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
50
50
  getEphemeralRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
51
+ getImportRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
51
52
 
52
53
  getFileStream(options: GetFileOptions): Promise<Response>;
53
54
  getArchiveStream(options?: ArchiveOptions): Promise<Response>;
@@ -96,7 +97,14 @@ export interface RepoOptions {
96
97
  createdAt?: string;
97
98
  }
98
99
 
99
- export type SupportedRepoProvider = 'github';
100
+ export type SupportedRepoProvider =
101
+ | 'github'
102
+ | 'gitlab'
103
+ | 'bitbucket'
104
+ | 'gitea'
105
+ | 'forgejo'
106
+ | 'codeberg'
107
+ | 'sr.ht';
100
108
 
101
109
  export interface PublicGitHubBaseRepoAuth {
102
110
  /**
@@ -109,20 +117,59 @@ export interface GitHubBaseRepo {
109
117
  /**
110
118
  * @default github
111
119
  */
112
- provider?: SupportedRepoProvider;
120
+ provider?: 'github';
113
121
  owner: string;
114
122
  name: string;
115
123
  defaultBranch?: string;
116
124
  auth?: PublicGitHubBaseRepoAuth;
117
125
  }
118
126
 
127
+ export interface GenericGitBaseRepo {
128
+ /**
129
+ * The git host provider. Must be one of the supported generic git providers.
130
+ */
131
+ provider: Exclude<SupportedRepoProvider, 'github'>;
132
+ owner: string;
133
+ name: string;
134
+ defaultBranch?: string;
135
+ /**
136
+ * Bare hostname for self-hosted instances (e.g. "gitlab.example.com").
137
+ * Falls back to the provider's default host when omitted.
138
+ */
139
+ upstreamHost?: string;
140
+ }
141
+
119
142
  export interface ForkBaseRepo {
120
143
  id: string;
121
144
  ref?: string;
122
145
  sha?: string;
123
146
  }
124
147
 
125
- export type BaseRepo = GitHubBaseRepo | ForkBaseRepo;
148
+ export type BaseRepo = GitHubBaseRepo | ForkBaseRepo | GenericGitBaseRepo;
149
+
150
+ export interface CreateGitCredentialOptions {
151
+ repoId: string;
152
+ username?: string;
153
+ password: string;
154
+ ttl?: number;
155
+ }
156
+
157
+ export interface UpdateGitCredentialOptions {
158
+ id: string;
159
+ username?: string;
160
+ password: string;
161
+ ttl?: number;
162
+ }
163
+
164
+ export interface DeleteGitCredentialOptions {
165
+ id: string;
166
+ ttl?: number;
167
+ }
168
+
169
+ export interface GitCredential {
170
+ id: string;
171
+ createdAt?: string;
172
+ }
126
173
 
127
174
  export interface ListReposOptions extends GitStorageInvocationOptions {
128
175
  cursor?: string;