diff --git a/README.md b/README.md index 8dbe570..233e70f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,10 @@ app.get('/users', { await app.register(pman, { postmanApiKey: 'PMAK-…', + // Either pass workspaceId directly... workspaceId: '00000000-0000-4000-8000-000000000000', + // ...or pass a workspace link and let pman extract the id: + // workspaceLink: 'https://.postman.co/workspace/My~00000000-0000-4000-8000-000000000000/overview', postmanBaseUrl: 'http://127.0.0.1:3000', collectionName: 'My API', folderStrategy: 'path', @@ -77,6 +80,7 @@ Secrets are never written to the sync state file. | Option | Description | |--------|-------------| | `workspaceId` | Postman workspace id | +| `workspaceLink` | Postman workspace link (extracts `workspaceId` automatically) | | `postmanApiKey` | Postman API key | | `postmanBaseUrl` | Value for Postman variable `baseUrl` (`{{baseUrl}}` in URLs) | | `reuseExistingCollectionByName` | Reuse workspace collection with same name when no state file (default `true`) | diff --git a/examples/playground.mjs b/examples/playground.mjs index 1db8946..83a5a3c 100644 --- a/examples/playground.mjs +++ b/examples/playground.mjs @@ -159,7 +159,7 @@ async function main() { postmanApiKey: postman.postmanApiKey, workspaceId: postman.workspaceId, postmanBaseUrl: publicBase, - collectionName: 'Shiftr / pman playground', + collectionName: 'pman ~ by st3ix', }); await fastify.listen({ port, host: '127.0.0.1' }); diff --git a/package-lock.json b/package-lock.json index d038af0..cf23bfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@st3ix/pman", - "version": "1.0.1", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 51e7b75..07b9e5a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@st3ix/pman", - "version": "1.0.1", + "version": "1.0.2", "description": "Sync Fastify route schemas to Postman via OpenAPI and the Postman API.", "type": "module", "main": "./dist/index.js", diff --git a/src/options.ts b/src/options.ts index b98880c..4eb60ad 100644 --- a/src/options.ts +++ b/src/options.ts @@ -4,6 +4,8 @@ export type FolderStrategy = 'path' | 'tags' | 'hybrid'; export type FastifyPmanOptions = { workspaceId?: string; + /** Optional Postman workspace URL. If set, the workspace id is extracted automatically. */ + workspaceLink?: string; collectionName?: string; statePath?: string; dryRun?: boolean; @@ -58,6 +60,36 @@ function firstNonEmpty(...candidates: (string | undefined)[]): string | undefine return undefined; } +function wspacelink(link: string | undefined): string | undefined { + if (typeof link !== 'string') return undefined; + const raw = link.trim(); + if (!raw) return undefined; + + let u: URL; + try { + u = new URL(raw); + } catch { + return undefined; + } + + const parts = u.pathname.split('/').filter(Boolean); + const i = parts.indexOf('workspace'); + if (i < 0) return undefined; + const seg = parts[i + 1]; + if (typeof seg !== 'string' || seg.length === 0) return undefined; + + // Typical format: "~" + const afterTilde = seg.includes('~') ? seg.split('~').pop() : seg; + const id = (afterTilde ?? '').trim(); + if (!id) return undefined; + + // Very small sanity check: UUID-ish. + if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) { + return undefined; + } + return id; +} + export function resolvePmanOptions(opts: FastifyPmanOptions): ResolvedPmanOptions { const auth = opts.auth && typeof opts.auth === 'object' @@ -73,8 +105,10 @@ export function resolvePmanOptions(opts: FastifyPmanOptions): ResolvedPmanOption } : null; + const extractedWorkspaceId = wspacelink(opts.workspaceLink); + return { - workspaceId: firstNonEmpty(opts.workspaceId, process.env.POSTMAN_WORKSPACE_ID), + workspaceId: firstNonEmpty(opts.workspaceId, extractedWorkspaceId, process.env.POSTMAN_WORKSPACE_ID), collectionName: opts.collectionName?.trim() || 'Fastify (pman)', statePath: opts.statePath ?? `${process.cwd()}/.postman-sync.json`, dryRun: opts.dryRun ?? false,