@theholocron/holocron-plugin-postman 2.0.0-alpha.8 → 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/README.md +19 -15
- package/dist/index.d.mts +7 -108
- package/dist/index.mjs +47 -205
- package/package.json +22 -10
package/README.md
CHANGED
|
@@ -5,8 +5,10 @@ Postman plugin for [Holocron](../cli). Implements the **multi-cardinality**
|
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
8
|
+
<!-- prettier-ignore -->
|
|
8
9
|
```bash
|
|
9
10
|
pnpm add -D @theholocron/holocron-plugin-postman@alpha
|
|
11
|
+
|
|
10
12
|
```
|
|
11
13
|
|
|
12
14
|
## Why REST, not the CLI
|
|
@@ -29,24 +31,26 @@ Generate the key at <https://web.postman.co/settings/me/api-keys>.
|
|
|
29
31
|
|
|
30
32
|
## Config
|
|
31
33
|
|
|
34
|
+
<!-- prettier-ignore -->
|
|
32
35
|
```jsonc
|
|
33
36
|
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
"providers": {
|
|
38
|
+
"tooling": [
|
|
39
|
+
[
|
|
40
|
+
"postman",
|
|
41
|
+
{
|
|
42
|
+
"workspaceId": "00000000-0000-0000-0000-000000000000",
|
|
43
|
+
"specFile": "apps/api/openapi.json",
|
|
44
|
+
"specName": "Rando API",
|
|
45
|
+
"collectionName": "Rando API",
|
|
46
|
+
"envFiles": ["apps/api/postman-env-staging.json"],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
"storybook",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
49
52
|
}
|
|
53
|
+
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
- `workspaceId` (required) — Postman workspace id. Find via `holocron tooling postman workspaces`.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,56 +1,8 @@
|
|
|
1
|
-
import { Tooling, ToolingDoctorReport } from "@theholocron/cli";
|
|
1
|
+
import { AuthError, ResolveTokenInput, Tooling, ToolingDoctorReport } from "@theholocron/cli";
|
|
2
|
+
import { PostmanClient, PostmanClient as PostmanClient$1, PostmanCollection, PostmanEnvironment, PostmanPlanLimitError, PostmanSpec, PostmanUser, PostmanWorkspace, createPostmanClient, detectPlanLimit } from "@theholocron/postman-client";
|
|
2
3
|
|
|
3
4
|
//#region src/auth.d.ts
|
|
4
|
-
|
|
5
|
-
* Token resolution for the Postman plugin.
|
|
6
|
-
*
|
|
7
|
-
* Resolution order (matches the standard 4-step precedence set by
|
|
8
|
-
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
9
|
-
* 1. explicit `cliToken` argument (from `--token` flag)
|
|
10
|
-
* 2. HOLOCRON_POSTMAN_API_KEY env var (preferred — explicit intent)
|
|
11
|
-
* 3. POSTMAN_API_KEY env var (Postman's own default)
|
|
12
|
-
* 4. keyring (com.theholocron.cli / "postman")
|
|
13
|
-
* 5. AuthError naming all four options + the bootstrap hint
|
|
14
|
-
*/
|
|
15
|
-
declare class AuthError extends Error {
|
|
16
|
-
name: string;
|
|
17
|
-
}
|
|
18
|
-
interface ResolveTokenInput {
|
|
19
|
-
/** From `--token` CLI flag. */
|
|
20
|
-
cliToken?: string;
|
|
21
|
-
/** Env vars; passed in for testability. Defaults to `process.env`. */
|
|
22
|
-
env?: NodeJS.ProcessEnv;
|
|
23
|
-
/** Keyring lookup fn; passed in for testability. Defaults to `getToken(provider)`. */
|
|
24
|
-
keyring?: (provider: string) => string | null;
|
|
25
|
-
}
|
|
26
|
-
declare function resolveToken(input?: ResolveTokenInput): string;
|
|
27
|
-
//#endregion
|
|
28
|
-
//#region src/rest.d.ts
|
|
29
|
-
/**
|
|
30
|
-
* Thin REST wrapper around api.getpostman.com.
|
|
31
|
-
*
|
|
32
|
-
* Same pattern as the github / vercel / neon / clerk REST clients —
|
|
33
|
-
* the Postman API uses `x-api-key` (not bearer) for auth, but
|
|
34
|
-
* otherwise the shape's identical: JSON only, transport-failure
|
|
35
|
-
* wrapping with `status: 0`.
|
|
36
|
-
*/
|
|
37
|
-
interface RestClientOptions {
|
|
38
|
-
token: string;
|
|
39
|
-
fetch?: typeof fetch;
|
|
40
|
-
baseUrl?: string;
|
|
41
|
-
}
|
|
42
|
-
interface RequestOptions {
|
|
43
|
-
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
44
|
-
body?: unknown;
|
|
45
|
-
query?: Record<string, string>;
|
|
46
|
-
}
|
|
47
|
-
declare class PostmanRestClient {
|
|
48
|
-
private readonly token;
|
|
49
|
-
private readonly fetchImpl;
|
|
50
|
-
readonly baseUrl: string;
|
|
51
|
-
constructor(opts: RestClientOptions);
|
|
52
|
-
request<T>(path: string, opts?: RequestOptions): Promise<T>;
|
|
53
|
-
}
|
|
5
|
+
declare const resolveToken: (input?: import("@theholocron/http-client").ResolveTokenInput) => string;
|
|
54
6
|
//#endregion
|
|
55
7
|
//#region src/capabilities/tooling.d.ts
|
|
56
8
|
interface PostmanToolingOptions {
|
|
@@ -66,38 +18,13 @@ interface PostmanToolingOptions {
|
|
|
66
18
|
/** Working repo root. Defaults to process.cwd(). */
|
|
67
19
|
repoRoot?: string;
|
|
68
20
|
}
|
|
69
|
-
interface PostmanUser {
|
|
70
|
-
id: number;
|
|
71
|
-
username: string;
|
|
72
|
-
fullName: string;
|
|
73
|
-
}
|
|
74
|
-
interface PostmanWorkspace {
|
|
75
|
-
id: string;
|
|
76
|
-
name: string;
|
|
77
|
-
type: string;
|
|
78
|
-
}
|
|
79
|
-
interface PostmanCollection {
|
|
80
|
-
id: string;
|
|
81
|
-
uid: string;
|
|
82
|
-
name: string;
|
|
83
|
-
}
|
|
84
|
-
interface PostmanEnvironment {
|
|
85
|
-
id: string;
|
|
86
|
-
uid: string;
|
|
87
|
-
name: string;
|
|
88
|
-
}
|
|
89
|
-
interface PostmanSpec {
|
|
90
|
-
id: string;
|
|
91
|
-
name: string;
|
|
92
|
-
type: string;
|
|
93
|
-
}
|
|
94
21
|
declare class PostmanTooling implements Tooling {
|
|
95
|
-
private readonly
|
|
22
|
+
private readonly client;
|
|
96
23
|
readonly key: "tooling";
|
|
97
24
|
readonly providerName = "postman";
|
|
98
25
|
private readonly opts;
|
|
99
26
|
private readonly repoRoot;
|
|
100
|
-
constructor(
|
|
27
|
+
constructor(client: PostmanClient$1, opts: PostmanToolingOptions);
|
|
101
28
|
sync(): Promise<void>;
|
|
102
29
|
doctor(): Promise<ToolingDoctorReport>;
|
|
103
30
|
getMyself(): Promise<PostmanUser>;
|
|
@@ -144,34 +71,6 @@ declare class PostmanTooling implements Tooling {
|
|
|
144
71
|
}): Promise<void>;
|
|
145
72
|
}
|
|
146
73
|
//#endregion
|
|
147
|
-
//#region src/errors.d.ts
|
|
148
|
-
/**
|
|
149
|
-
* Postman-specific errors that callers might want to discriminate.
|
|
150
|
-
*/
|
|
151
|
-
/**
|
|
152
|
-
* Thrown when Postman returns a `limitReachedError` (e.g., a Free-tier
|
|
153
|
-
* account hitting the "0 APIs" cap). Callers can render "upgrade
|
|
154
|
-
* required" instead of dumping the raw API body.
|
|
155
|
-
*/
|
|
156
|
-
declare class PostmanPlanLimitError extends Error {
|
|
157
|
-
/** Human-readable plan-limit message from Postman. */
|
|
158
|
-
readonly limitMessage: string;
|
|
159
|
-
/** Original response body (JSON or text). */
|
|
160
|
-
readonly body: string;
|
|
161
|
-
name: string;
|
|
162
|
-
constructor(/** Human-readable plan-limit message from Postman. */
|
|
163
|
-
|
|
164
|
-
limitMessage: string, /** Original response body (JSON or text). */
|
|
165
|
-
|
|
166
|
-
body: string);
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Inspect a Postman error body for the plan-limit shape. Returns the
|
|
170
|
-
* limit message when matched; null otherwise (caller throws a generic
|
|
171
|
-
* `ProviderApiError`).
|
|
172
|
-
*/
|
|
173
|
-
declare function detectPlanLimit(body: string): string | null;
|
|
174
|
-
//#endregion
|
|
175
74
|
//#region src/verify-token.d.ts
|
|
176
75
|
/**
|
|
177
76
|
* `verifyToken` — plugin-level export used by `holocron auth set` +
|
|
@@ -204,7 +103,7 @@ interface PostmanPluginOptions extends ResolveTokenInput, PostmanToolingOptions
|
|
|
204
103
|
}
|
|
205
104
|
interface PluginContext {
|
|
206
105
|
options: PostmanPluginOptions;
|
|
207
|
-
|
|
106
|
+
client: PostmanClient;
|
|
208
107
|
}
|
|
209
108
|
declare function createContext(options: PostmanPluginOptions): PluginContext;
|
|
210
109
|
declare function tooling(ctx: PluginContext): Tooling;
|
|
@@ -220,4 +119,4 @@ declare function createPlugin(options: PostmanPluginOptions): {
|
|
|
220
119
|
*/
|
|
221
120
|
declare const AUTH_HINT: string;
|
|
222
121
|
//#endregion
|
|
223
|
-
export { AUTH_HINT, AuthError, PluginContext, PostmanPlanLimitError, PostmanPluginOptions,
|
|
122
|
+
export { AUTH_HINT, AuthError, PluginContext, type PostmanClient, PostmanPlanLimitError, PostmanPluginOptions, PostmanTooling, type ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createContext, createPlugin, createPostmanClient, detectPlanLimit, resolveToken, tooling, verifyToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
|
-
import { ProviderApiError,
|
|
1
|
+
import { AuthError, ProviderApiError, createResolveToken } from "@theholocron/cli";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { basename, resolve } from "node:path";
|
|
4
|
+
import { PostmanPlanLimitError, createPostmanClient, detectPlanLimit } from "@theholocron/postman-client";
|
|
4
5
|
//#region src/auth.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* 2. HOLOCRON_POSTMAN_API_KEY env var (preferred — explicit intent)
|
|
12
|
-
* 3. POSTMAN_API_KEY env var (Postman's own default)
|
|
13
|
-
* 4. keyring (com.theholocron.cli / "postman")
|
|
14
|
-
* 5. AuthError naming all four options + the bootstrap hint
|
|
15
|
-
*/
|
|
16
|
-
var AuthError = class extends Error {
|
|
17
|
-
name = "AuthError";
|
|
18
|
-
};
|
|
19
|
-
function resolveToken(input = {}) {
|
|
20
|
-
const env = input.env ?? process.env;
|
|
21
|
-
const keyring = input.keyring ?? getToken;
|
|
22
|
-
const token = input.cliToken || env["HOLOCRON_POSTMAN_API_KEY"] || env["POSTMAN_API_KEY"] || keyring("postman");
|
|
23
|
-
if (!token) throw new AuthError("no Postman API key found. Pass --token <KEY>, set HOLOCRON_POSTMAN_API_KEY / POSTMAN_API_KEY, or run: holocron auth set postman <KEY>");
|
|
24
|
-
return token;
|
|
25
|
-
}
|
|
6
|
+
const resolveToken = createResolveToken({
|
|
7
|
+
envName: "HOLOCRON_POSTMAN_API_KEY",
|
|
8
|
+
vendorEnvName: "POSTMAN_API_KEY",
|
|
9
|
+
keyringService: "postman",
|
|
10
|
+
errorMessage: "no Postman API key found. Pass --token <KEY>, set HOLOCRON_POSTMAN_API_KEY / POSTMAN_API_KEY, or run: holocron auth set postman <KEY>"
|
|
11
|
+
});
|
|
26
12
|
//#endregion
|
|
27
13
|
//#region src/capabilities/tooling.ts
|
|
28
14
|
/**
|
|
@@ -48,13 +34,13 @@ function resolveToken(input = {}) {
|
|
|
48
34
|
* reachability and returns a `ToolingDoctorReport`.
|
|
49
35
|
*/
|
|
50
36
|
var PostmanTooling = class {
|
|
51
|
-
|
|
37
|
+
client;
|
|
52
38
|
key = "tooling";
|
|
53
39
|
providerName = "postman";
|
|
54
40
|
opts;
|
|
55
41
|
repoRoot;
|
|
56
|
-
constructor(
|
|
57
|
-
this.
|
|
42
|
+
constructor(client, opts) {
|
|
43
|
+
this.client = client;
|
|
58
44
|
if (!opts.workspaceId) throw new Error("PostmanTooling requires `workspaceId` in options");
|
|
59
45
|
this.opts = opts;
|
|
60
46
|
this.repoRoot = opts.repoRoot ?? process.cwd();
|
|
@@ -128,200 +114,54 @@ var PostmanTooling = class {
|
|
|
128
114
|
}
|
|
129
115
|
}
|
|
130
116
|
async getMyself() {
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
id: raw.user.id,
|
|
134
|
-
username: raw.user.username,
|
|
135
|
-
fullName: raw.user.fullName
|
|
136
|
-
};
|
|
117
|
+
return (await this.client.me.get()).user ?? {};
|
|
137
118
|
}
|
|
138
119
|
async listWorkspaces() {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
name: w.name,
|
|
142
|
-
type: w.type
|
|
143
|
-
}));
|
|
120
|
+
const { workspaces } = await this.client.workspaces.list();
|
|
121
|
+
return workspaces;
|
|
144
122
|
}
|
|
145
123
|
async findCollectionByName(input) {
|
|
146
|
-
const
|
|
147
|
-
return
|
|
148
|
-
id: match.id,
|
|
149
|
-
uid: match.uid,
|
|
150
|
-
name: match.name
|
|
151
|
-
} : null;
|
|
124
|
+
const { collections } = await this.client.collections.list(input.workspaceId);
|
|
125
|
+
return collections.find((c) => c.name === input.name) ?? null;
|
|
152
126
|
}
|
|
153
127
|
async deleteCollection(uid) {
|
|
154
|
-
await this.
|
|
128
|
+
await this.client.collections.delete(uid);
|
|
155
129
|
}
|
|
156
130
|
async importOpenApi(input) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
query: { workspace: input.workspaceId },
|
|
160
|
-
body: {
|
|
161
|
-
type: "string",
|
|
162
|
-
input: typeof input.spec === "string" ? input.spec : JSON.stringify(input.spec)
|
|
163
|
-
}
|
|
164
|
-
})).collections[0];
|
|
131
|
+
const { collections } = await this.client.import.openapi(input.workspaceId, input.spec);
|
|
132
|
+
const created = collections[0];
|
|
165
133
|
if (!created) throw new ProviderApiError("Postman returned no collection on import — check the OpenAPI spec is well-formed", 500, void 0);
|
|
166
|
-
return
|
|
167
|
-
id: created.id,
|
|
168
|
-
uid: created.uid,
|
|
169
|
-
name: created.name
|
|
170
|
-
};
|
|
134
|
+
return created;
|
|
171
135
|
}
|
|
172
136
|
async listEnvironments(input) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
uid: e.uid,
|
|
176
|
-
name: e.name
|
|
177
|
-
}));
|
|
137
|
+
const { environments } = await this.client.environments.list(input.workspaceId);
|
|
138
|
+
return environments;
|
|
178
139
|
}
|
|
179
140
|
async findEnvironmentByName(input) {
|
|
180
141
|
return (await this.listEnvironments(input)).find((e) => e.name === input.name) ?? null;
|
|
181
142
|
}
|
|
182
143
|
async createEnvironment(input) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
query: { workspace: input.workspaceId },
|
|
186
|
-
body: { environment: input.environment }
|
|
187
|
-
});
|
|
188
|
-
return {
|
|
189
|
-
id: raw.environment.id,
|
|
190
|
-
uid: raw.environment.uid,
|
|
191
|
-
name: raw.environment.name
|
|
192
|
-
};
|
|
144
|
+
const { environment } = await this.client.environments.create(input.workspaceId, input.environment);
|
|
145
|
+
return environment;
|
|
193
146
|
}
|
|
194
147
|
async updateEnvironment(input) {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
body: { environment: input.environment }
|
|
198
|
-
});
|
|
199
|
-
return {
|
|
200
|
-
id: raw.environment.id,
|
|
201
|
-
uid: raw.environment.uid,
|
|
202
|
-
name: raw.environment.name
|
|
203
|
-
};
|
|
148
|
+
const { environment } = await this.client.environments.update(input.uid, input.environment);
|
|
149
|
+
return environment;
|
|
204
150
|
}
|
|
205
151
|
async findSpecByName(input) {
|
|
206
|
-
const
|
|
207
|
-
return
|
|
208
|
-
id: match.id,
|
|
209
|
-
name: match.name,
|
|
210
|
-
type: match.type
|
|
211
|
-
} : null;
|
|
152
|
+
const { specs } = await this.client.specs.list(input.workspaceId);
|
|
153
|
+
return specs.find((s) => s.name === input.name) ?? null;
|
|
212
154
|
}
|
|
213
155
|
async createSpec(input) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
type: input.type ?? "OPENAPI:3.0",
|
|
220
|
-
files: [{
|
|
221
|
-
path: input.filePath ?? "index.json",
|
|
222
|
-
content: input.fileContent
|
|
223
|
-
}]
|
|
224
|
-
}
|
|
156
|
+
return this.client.specs.create(input.workspaceId, {
|
|
157
|
+
name: input.name,
|
|
158
|
+
type: input.type,
|
|
159
|
+
filePath: input.filePath,
|
|
160
|
+
fileContent: input.fileContent
|
|
225
161
|
});
|
|
226
|
-
return {
|
|
227
|
-
id: raw.id,
|
|
228
|
-
name: raw.name,
|
|
229
|
-
type: raw.type
|
|
230
|
-
};
|
|
231
162
|
}
|
|
232
163
|
async upsertSpecFile(input) {
|
|
233
|
-
await this.
|
|
234
|
-
method: "PATCH",
|
|
235
|
-
body: { content: input.content }
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
//#endregion
|
|
240
|
-
//#region src/errors.ts
|
|
241
|
-
/**
|
|
242
|
-
* Postman-specific errors that callers might want to discriminate.
|
|
243
|
-
*/
|
|
244
|
-
/**
|
|
245
|
-
* Thrown when Postman returns a `limitReachedError` (e.g., a Free-tier
|
|
246
|
-
* account hitting the "0 APIs" cap). Callers can render "upgrade
|
|
247
|
-
* required" instead of dumping the raw API body.
|
|
248
|
-
*/
|
|
249
|
-
var PostmanPlanLimitError = class extends Error {
|
|
250
|
-
limitMessage;
|
|
251
|
-
body;
|
|
252
|
-
name = "PostmanPlanLimitError";
|
|
253
|
-
constructor(limitMessage, body) {
|
|
254
|
-
super(limitMessage);
|
|
255
|
-
this.limitMessage = limitMessage;
|
|
256
|
-
this.body = body;
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
/**
|
|
260
|
-
* Inspect a Postman error body for the plan-limit shape. Returns the
|
|
261
|
-
* limit message when matched; null otherwise (caller throws a generic
|
|
262
|
-
* `ProviderApiError`).
|
|
263
|
-
*/
|
|
264
|
-
function detectPlanLimit(body) {
|
|
265
|
-
try {
|
|
266
|
-
const parsed = JSON.parse(body);
|
|
267
|
-
if (parsed.error?.name === "limitReachedError" && parsed.error.message) return parsed.error.message;
|
|
268
|
-
} catch {}
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
//#endregion
|
|
272
|
-
//#region src/rest.ts
|
|
273
|
-
/**
|
|
274
|
-
* Thin REST wrapper around api.getpostman.com.
|
|
275
|
-
*
|
|
276
|
-
* Same pattern as the github / vercel / neon / clerk REST clients —
|
|
277
|
-
* the Postman API uses `x-api-key` (not bearer) for auth, but
|
|
278
|
-
* otherwise the shape's identical: JSON only, transport-failure
|
|
279
|
-
* wrapping with `status: 0`.
|
|
280
|
-
*/
|
|
281
|
-
var PostmanRestClient = class {
|
|
282
|
-
token;
|
|
283
|
-
fetchImpl;
|
|
284
|
-
baseUrl;
|
|
285
|
-
constructor(opts) {
|
|
286
|
-
this.token = opts.token;
|
|
287
|
-
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
288
|
-
let url = opts.baseUrl ?? "https://api.getpostman.com";
|
|
289
|
-
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
290
|
-
this.baseUrl = url;
|
|
291
|
-
}
|
|
292
|
-
async request(path, opts = {}) {
|
|
293
|
-
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`);
|
|
294
|
-
for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v);
|
|
295
|
-
const fullUrl = url.toString();
|
|
296
|
-
const headers = {
|
|
297
|
-
"x-api-key": this.token,
|
|
298
|
-
accept: "application/json"
|
|
299
|
-
};
|
|
300
|
-
const init = {
|
|
301
|
-
method: opts.method ?? "GET",
|
|
302
|
-
headers
|
|
303
|
-
};
|
|
304
|
-
if (opts.body !== void 0) {
|
|
305
|
-
headers["content-type"] = "application/json";
|
|
306
|
-
init.body = JSON.stringify(opts.body);
|
|
307
|
-
}
|
|
308
|
-
let res;
|
|
309
|
-
try {
|
|
310
|
-
res = await this.fetchImpl(fullUrl, init);
|
|
311
|
-
} catch (err) {
|
|
312
|
-
const detail = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
313
|
-
throw new ProviderApiError(`Postman ${init.method} ${path} failed: ${detail}`, 0, void 0);
|
|
314
|
-
}
|
|
315
|
-
if (!res.ok) {
|
|
316
|
-
const body = await res.text().catch(() => "");
|
|
317
|
-
const limit = detectPlanLimit(body);
|
|
318
|
-
if (limit) throw new PostmanPlanLimitError(limit, body);
|
|
319
|
-
throw new ProviderApiError(`Postman ${init.method} ${path} → ${res.status}`, res.status, body);
|
|
320
|
-
}
|
|
321
|
-
if (res.status === 204) return void 0;
|
|
322
|
-
const text = await res.text();
|
|
323
|
-
if (!text) return void 0;
|
|
324
|
-
return JSON.parse(text);
|
|
164
|
+
await this.client.specs.updateFile(input.specId, input.filePath, input.content);
|
|
325
165
|
}
|
|
326
166
|
};
|
|
327
167
|
//#endregion
|
|
@@ -332,12 +172,13 @@ var PostmanRestClient = class {
|
|
|
332
172
|
* authenticated user).
|
|
333
173
|
*/
|
|
334
174
|
async function verifyToken(token, opts = {}) {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
175
|
+
const client = createPostmanClient({
|
|
176
|
+
token,
|
|
177
|
+
baseUrl: opts.baseUrl,
|
|
178
|
+
fetch: opts.fetch
|
|
179
|
+
});
|
|
339
180
|
try {
|
|
340
|
-
const res = await
|
|
181
|
+
const res = await client.me.get();
|
|
341
182
|
return {
|
|
342
183
|
ok: true,
|
|
343
184
|
subject: `user @ ${res?.user?.email ?? res?.user?.username ?? res?.user?.fullName ?? String(res?.user?.id ?? "unknown")}`
|
|
@@ -353,12 +194,13 @@ async function verifyToken(token, opts = {}) {
|
|
|
353
194
|
//#region src/index.ts
|
|
354
195
|
function createContext(options) {
|
|
355
196
|
if (!options.workspaceId) throw new Error("@theholocron/holocron-plugin-postman requires `workspaceId` in options");
|
|
356
|
-
const restOpts = { token: resolveToken(options) };
|
|
357
|
-
if (options.baseUrl !== void 0) restOpts.baseUrl = options.baseUrl;
|
|
358
|
-
if (options.fetch !== void 0) restOpts.fetch = options.fetch;
|
|
359
197
|
return {
|
|
360
198
|
options,
|
|
361
|
-
|
|
199
|
+
client: createPostmanClient({
|
|
200
|
+
token: resolveToken(options),
|
|
201
|
+
baseUrl: options.baseUrl,
|
|
202
|
+
fetch: options.fetch
|
|
203
|
+
})
|
|
362
204
|
};
|
|
363
205
|
}
|
|
364
206
|
function tooling(ctx) {
|
|
@@ -368,7 +210,7 @@ function tooling(ctx) {
|
|
|
368
210
|
if (ctx.options.collectionName !== void 0) opts.collectionName = ctx.options.collectionName;
|
|
369
211
|
if (ctx.options.envFiles !== void 0) opts.envFiles = ctx.options.envFiles;
|
|
370
212
|
if (ctx.options.repoRoot !== void 0) opts.repoRoot = ctx.options.repoRoot;
|
|
371
|
-
return new PostmanTooling(ctx.
|
|
213
|
+
return new PostmanTooling(ctx.client, opts);
|
|
372
214
|
}
|
|
373
215
|
function createPlugin(options) {
|
|
374
216
|
const ctx = createContext(options);
|
|
@@ -383,4 +225,4 @@ function createPlugin(options) {
|
|
|
383
225
|
*/
|
|
384
226
|
const AUTH_HINT = "generate a Postman API key at https://postman.co/settings/me/api-keys, then run: holocron auth set postman <KEY>";
|
|
385
227
|
//#endregion
|
|
386
|
-
export { AUTH_HINT, AuthError, PostmanPlanLimitError,
|
|
228
|
+
export { AUTH_HINT, AuthError, PostmanPlanLimitError, PostmanTooling, createContext, createPlugin, createPostmanClient, detectPlanLimit, resolveToken, tooling, verifyToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-postman",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Holocron plugin for Postman. Implements the tooling capability against Postman's REST API — workspace + collection + spec + environment sync.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-postman#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
@@ -21,19 +21,31 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@theholocron/
|
|
24
|
+
"@theholocron/postman-client": "^1.1.0",
|
|
25
|
+
"@theholocron/cli": "2.0.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependenciesMeta": {
|
|
28
|
+
"@theholocron/postman-client": {
|
|
29
|
+
"optional": false
|
|
30
|
+
}
|
|
25
31
|
},
|
|
26
32
|
"devDependencies": {
|
|
27
|
-
"@theholocron/
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"vitest": "^
|
|
33
|
+
"@theholocron/eslint-config": "^7.3.0",
|
|
34
|
+
"@theholocron/postman-client": "^1.1.0",
|
|
35
|
+
"@theholocron/tsconfig": "^7.3.0",
|
|
36
|
+
"@theholocron/tsdown-config": "^7.3.0",
|
|
37
|
+
"@theholocron/vitest-config": "^7.3.0",
|
|
38
|
+
"@types/node": "^26",
|
|
39
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
40
|
+
"@vitest/eslint-plugin": "^1.6.23",
|
|
41
|
+
"eslint": "^10.7.0",
|
|
42
|
+
"eslint-plugin-n": "^18.2.2",
|
|
43
|
+
"globals": "^17.7.0",
|
|
34
44
|
"tsdown": "^0.22.3",
|
|
35
45
|
"tsx": "^4.22.4",
|
|
36
|
-
"
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vitest": "^4.1.10",
|
|
48
|
+
"@theholocron/cli": "2.0.0"
|
|
37
49
|
},
|
|
38
50
|
"publishConfig": {
|
|
39
51
|
"access": "public"
|