@shipfox/api-integration-spi 1.1.0 → 2.0.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +23 -0
- package/dist/contracts.d.ts +31 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +12 -2
- package/dist/contracts.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/contracts.test.ts +24 -0
- package/src/contracts.ts +38 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-integration-spi",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@shipfox/api-integration-core-dto": "
|
|
21
|
+
"@shipfox/api-integration-core-dto": "14.0.0"
|
|
22
22
|
},
|
|
23
23
|
"imports": {
|
|
24
24
|
"#*": "./dist/*"
|
package/src/contracts.test.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
IntegrationProviderError,
|
|
4
4
|
isValidGitObjectId,
|
|
5
5
|
isValidGitRefName,
|
|
6
|
+
isValidResolvableRef,
|
|
6
7
|
isValidTriggerRef,
|
|
7
8
|
parseProviderRepositoryId,
|
|
8
9
|
} from './contracts.js';
|
|
@@ -29,6 +30,9 @@ describe('git ref names', () => {
|
|
|
29
30
|
'refs/heads/.foo',
|
|
30
31
|
'refs/heads/foo.',
|
|
31
32
|
'refs/heads/foo@{bar',
|
|
33
|
+
'a'.repeat(40),
|
|
34
|
+
'b'.repeat(64),
|
|
35
|
+
'0'.repeat(40),
|
|
32
36
|
])('rejects %s', (ref) => {
|
|
33
37
|
expect(isValidGitRefName(ref)).toBe(false);
|
|
34
38
|
});
|
|
@@ -38,6 +42,26 @@ describe('git ref names', () => {
|
|
|
38
42
|
});
|
|
39
43
|
});
|
|
40
44
|
|
|
45
|
+
describe('resolvable refs', () => {
|
|
46
|
+
it.each([
|
|
47
|
+
'refs/heads/main',
|
|
48
|
+
'refs/heads/feature/review',
|
|
49
|
+
'refs/tags/v1.0.0',
|
|
50
|
+
])('accepts %s', (ref) => {
|
|
51
|
+
expect(isValidResolvableRef(ref)).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it.each(['refs/pull/17/head'])('rejects %s', (ref) => {
|
|
55
|
+
expect(isValidResolvableRef(ref)).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('rejects every name a trigger ref rejects', () => {
|
|
59
|
+
for (const ref of ['', 'HEAD', '-main', 'refs/heads/foo bar', 'refs/heads/foo..bar']) {
|
|
60
|
+
expect(isValidResolvableRef(ref)).toBe(false);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
41
65
|
describe('git object ids', () => {
|
|
42
66
|
it.each(['a'.repeat(40), 'b'.repeat(64)])('accepts a full object id', (value) => {
|
|
43
67
|
expect(isValidGitObjectId(value)).toBe(true);
|
package/src/contracts.ts
CHANGED
|
@@ -53,6 +53,16 @@ export interface ResolveRepositoryInput<
|
|
|
53
53
|
externalRepositoryId: string;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export interface ResolveRefInput<Connection extends IntegrationConnection = IntegrationConnection>
|
|
57
|
+
extends ResolveRepositoryInput<Connection> {
|
|
58
|
+
ref: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ResolvedRef {
|
|
62
|
+
ref: string;
|
|
63
|
+
commit: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
export interface FileSnapshot {
|
|
57
67
|
path: string;
|
|
58
68
|
ref: string;
|
|
@@ -129,6 +139,11 @@ export interface SourceControlProvider<
|
|
|
129
139
|
listFiles(input: ListFilesInput<Connection>): Promise<FilePage>;
|
|
130
140
|
fetchFile(input: FetchFileInput<Connection>): Promise<FileSnapshot>;
|
|
131
141
|
resolveTriggerReference(payload: unknown): TriggerReference | null;
|
|
142
|
+
/**
|
|
143
|
+
* Pins a branch or tag name to the commit it currently points at.
|
|
144
|
+
* The snapshot can become unreachable before a caller uses it.
|
|
145
|
+
*/
|
|
146
|
+
resolveRef(input: ResolveRefInput<Connection>): Promise<ResolvedRef>;
|
|
132
147
|
createCheckoutSpec?(input: CreateCheckoutSpecInput<Connection>): Promise<CheckoutSpec>;
|
|
133
148
|
}
|
|
134
149
|
|
|
@@ -223,6 +238,13 @@ export interface IntegrationProvider<
|
|
|
223
238
|
> {
|
|
224
239
|
provider: ProviderKind;
|
|
225
240
|
displayName: string;
|
|
241
|
+
/**
|
|
242
|
+
* The event names this provider documents for its handler.
|
|
243
|
+
* Provider-minted names are never treated as a closed set by validation; the
|
|
244
|
+
* catalog is a curated diagnostic aid and may omit provider events. Providers
|
|
245
|
+
* without a documented catalog, including built-in trigger sources, omit it.
|
|
246
|
+
*/
|
|
247
|
+
eventCatalog?: import('@shipfox/api-integration-core-dto').IntegrationEventCatalog | undefined;
|
|
226
248
|
adapters?: IntegrationProviderAdapters<Connection> | undefined;
|
|
227
249
|
routes?: Route[] | undefined;
|
|
228
250
|
connectionExternalUrl?(connection: Connection): Promise<string | undefined>;
|
|
@@ -249,10 +271,13 @@ export type IntegrationProviderErrorReason =
|
|
|
249
271
|
| 'repository-not-found'
|
|
250
272
|
| 'installation-not-found'
|
|
251
273
|
| 'file-not-found'
|
|
274
|
+
| 'ref-not-found'
|
|
275
|
+
| 'ref-invalid'
|
|
252
276
|
| 'access-denied'
|
|
253
277
|
| 'rate-limited'
|
|
254
278
|
| 'timeout'
|
|
255
279
|
| 'provider-unavailable'
|
|
280
|
+
| 'provider-rejected'
|
|
256
281
|
| 'malformed-provider-response'
|
|
257
282
|
| 'content-too-large'
|
|
258
283
|
| 'too-many-files';
|
|
@@ -262,6 +287,7 @@ export class IntegrationProviderError extends Error {
|
|
|
262
287
|
public readonly reason: IntegrationProviderErrorReason,
|
|
263
288
|
message: string,
|
|
264
289
|
public readonly retryAfterSeconds?: number | undefined,
|
|
290
|
+
public readonly status?: number | undefined,
|
|
265
291
|
) {
|
|
266
292
|
super(message);
|
|
267
293
|
this.name = 'IntegrationProviderError';
|
|
@@ -363,6 +389,18 @@ export function isValidTriggerRef(ref: string): boolean {
|
|
|
363
389
|
return isValidGitRefName(ref);
|
|
364
390
|
}
|
|
365
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Validates a ref the platform may resolve to a commit: any safe git ref name
|
|
394
|
+
* except pull-request refs. Raw object ids do not satisfy the safe git ref
|
|
395
|
+
* name rules because valid refs must contain a slash. `refs/pull/N/head` can
|
|
396
|
+
* point at a fork and providers serve any commit in the repository network by
|
|
397
|
+
* SHA, so accepting names only keeps resolutions on refs a member pushed to
|
|
398
|
+
* the project repository.
|
|
399
|
+
*/
|
|
400
|
+
export function isValidResolvableRef(ref: string): boolean {
|
|
401
|
+
return isValidTriggerRef(ref) && !ref.startsWith('refs/pull/');
|
|
402
|
+
}
|
|
403
|
+
|
|
366
404
|
export function isValidGitObjectId(value: string): boolean {
|
|
367
405
|
return GIT_OBJECT_ID_PATTERN.test(value) && !ZERO_GIT_OBJECT_ID_PATTERN.test(value);
|
|
368
406
|
}
|