contree-client 0.1.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/LICENSE +177 -0
- package/README.md +131 -0
- package/lib/client.d.ts +181 -0
- package/lib/client.js +786 -0
- package/lib/errors.d.ts +30 -0
- package/lib/errors.js +46 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.js +18 -0
- package/lib/models.d.ts +811 -0
- package/lib/models.js +2173 -0
- package/lib/operations.d.ts +193 -0
- package/lib/operations.js +543 -0
- package/lib/profiles.d.ts +31 -0
- package/lib/profiles.js +159 -0
- package/lib/runtime.d.ts +93 -0
- package/lib/runtime.js +415 -0
- package/lib/specInfo.d.ts +4 -0
- package/lib/specInfo.js +8 -0
- package/lib/testing.d.ts +37 -0
- package/lib/testing.js +122 -0
- package/package.json +35 -0
package/lib/testing.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ContreeClient as GeneratedClient } from "./client.js";
|
|
2
|
+
import type { RetryPolicy } from "./runtime.js";
|
|
3
|
+
|
|
4
|
+
export declare const RESERVED: Set<string>;
|
|
5
|
+
|
|
6
|
+
export interface Outcome {
|
|
7
|
+
result: unknown;
|
|
8
|
+
error: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Call {
|
|
12
|
+
operation: string;
|
|
13
|
+
args: unknown[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export declare class ContreeClient extends GeneratedClient {
|
|
17
|
+
mocks: Map<string, Outcome[]>;
|
|
18
|
+
calls: Call[];
|
|
19
|
+
constructedWith: {
|
|
20
|
+
token: string;
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
project: string | null;
|
|
23
|
+
timeout: number | null;
|
|
24
|
+
retry: RetryPolicy | null;
|
|
25
|
+
identity: string | null;
|
|
26
|
+
};
|
|
27
|
+
constructor(
|
|
28
|
+
token?: string,
|
|
29
|
+
options?: ConstructorParameters<typeof GeneratedClient>[1],
|
|
30
|
+
);
|
|
31
|
+
mock(
|
|
32
|
+
operation: string,
|
|
33
|
+
result?: unknown,
|
|
34
|
+
options?: { error?: unknown },
|
|
35
|
+
): this;
|
|
36
|
+
callsFor(operation: string): Call[];
|
|
37
|
+
}
|
package/lib/testing.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/** An offline test double with the exact generated client surface.
|
|
2
|
+
*
|
|
3
|
+
* Every API method is replaced by a wrapper that records the call and
|
|
4
|
+
* returns the next mocked outcome for that operation. The last
|
|
5
|
+
* outcome in a queue is sticky, so a single mock serves any number of
|
|
6
|
+
* calls while a queue models state transitions (EXECUTING -> SUCCESS).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ContreeClient as GeneratedClient } from "./client.js";
|
|
10
|
+
import { ContreeError } from "./errors.js";
|
|
11
|
+
|
|
12
|
+
export const RESERVED = new Set([
|
|
13
|
+
"constructor",
|
|
14
|
+
"request",
|
|
15
|
+
"stream",
|
|
16
|
+
"open",
|
|
17
|
+
"close",
|
|
18
|
+
"mock",
|
|
19
|
+
"callsFor",
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const ASYNC_GENERATOR = async function* generatorProbe() {}.constructor;
|
|
23
|
+
|
|
24
|
+
function apiMethodNames() {
|
|
25
|
+
return Object.getOwnPropertyNames(GeneratedClient.prototype).filter(
|
|
26
|
+
(name) =>
|
|
27
|
+
!RESERVED.has(name) &&
|
|
28
|
+
!name.startsWith("_") &&
|
|
29
|
+
typeof GeneratedClient.prototype[name] === "function",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function unmocked(operation) {
|
|
34
|
+
return new ContreeError(
|
|
35
|
+
`no mock configured for ${operation}(); arm it with` +
|
|
36
|
+
` client.mock(${JSON.stringify(operation)}, result)`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class ContreeClient extends GeneratedClient {
|
|
41
|
+
constructor(token = "test-token", options = {}) {
|
|
42
|
+
super(token, options);
|
|
43
|
+
this.mocks = new Map();
|
|
44
|
+
this.calls = [];
|
|
45
|
+
// how the double was constructed, for assertions in tests of
|
|
46
|
+
// code that builds clients itself (factories, fromProfile)
|
|
47
|
+
this.constructedWith = {
|
|
48
|
+
token,
|
|
49
|
+
baseUrl: this.baseUrl,
|
|
50
|
+
project: this.project,
|
|
51
|
+
timeout: this.timeout,
|
|
52
|
+
retry: this.retry,
|
|
53
|
+
identity: this.identity,
|
|
54
|
+
};
|
|
55
|
+
for (const name of apiMethodNames()) {
|
|
56
|
+
const method = GeneratedClient.prototype[name];
|
|
57
|
+
this[name] =
|
|
58
|
+
method instanceof ASYNC_GENERATOR
|
|
59
|
+
? this.iterWrapper(name)
|
|
60
|
+
: this.callWrapper(name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
record(operation, args) {
|
|
65
|
+
this.calls.push({ operation, args });
|
|
66
|
+
const queue = this.mocks.get(operation);
|
|
67
|
+
if (queue === undefined || queue.length === 0) {
|
|
68
|
+
throw unmocked(operation);
|
|
69
|
+
}
|
|
70
|
+
return queue.length > 1 ? queue.shift() : queue[0];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
callWrapper(operation) {
|
|
74
|
+
return async (...args) => {
|
|
75
|
+
const outcome = this.record(operation, args);
|
|
76
|
+
if (outcome.error !== null) {
|
|
77
|
+
throw outcome.error;
|
|
78
|
+
}
|
|
79
|
+
return outcome.result;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
iterWrapper(operation) {
|
|
84
|
+
const self = this;
|
|
85
|
+
return async function* wrapper(...args) {
|
|
86
|
+
const outcome = self.record(operation, args);
|
|
87
|
+
yield* outcome.result ?? [];
|
|
88
|
+
if (outcome.error !== null) {
|
|
89
|
+
throw outcome.error;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Queue an outcome for *operation*; the last one queued is sticky. */
|
|
95
|
+
mock(operation, result = null, { error = null } = {}) {
|
|
96
|
+
if (typeof this[operation] !== "function" || RESERVED.has(operation)) {
|
|
97
|
+
throw new ContreeError(
|
|
98
|
+
`unknown API operation ${JSON.stringify(operation)}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const queue = this.mocks.get(operation) ?? [];
|
|
102
|
+
queue.push({ result, error });
|
|
103
|
+
this.mocks.set(operation, queue);
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** All recorded calls of *operation*, in order. */
|
|
108
|
+
callsFor(operation) {
|
|
109
|
+
return this.calls.filter((call) => call.operation === operation);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async request(spec) {
|
|
113
|
+
throw unmocked(`${spec.method} ${spec.path}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// eslint-disable-next-line require-yield
|
|
117
|
+
async *stream(spec) {
|
|
118
|
+
throw unmocked(`${spec.method} ${spec.path}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async close() {}
|
|
122
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "contree-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript client for the Contree API, generated from the OpenAPI spec",
|
|
5
|
+
"homepage": "https://contree.dev/",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./testing": {
|
|
14
|
+
"types": "./lib/testing.d.ts",
|
|
15
|
+
"default": "./lib/testing.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"types": "./lib/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"lib"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18.17"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"format": "prettier --write lib test",
|
|
27
|
+
"lint": "prettier --check lib test && tsc --noEmit",
|
|
28
|
+
"test": "node --test test/*.test.mjs",
|
|
29
|
+
"prepack": "node --input-type=module -e \"await import(new URL('./lib/index.js', 'file://' + process.cwd() + '/').href); await import(new URL('./lib/testing.js', 'file://' + process.cwd() + '/').href);\""
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"prettier": "^3.3.0",
|
|
33
|
+
"typescript": "^5.5.0"
|
|
34
|
+
}
|
|
35
|
+
}
|