@frogbotai/plugin-api-keys 0.25.0 → 0.27.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 CHANGED
@@ -25,7 +25,7 @@ Plaintext keys are returned only when created. The collection stores SHA-256 has
25
25
 
26
26
  With AI configured, usage rows include an optional `apiKey` relationship to the authenticating key. Cookie-authenticated requests omit it, and the relationship follows `collectionSlug`.
27
27
 
28
- Build and start-only workflows must run `frogbot generate:importmap` after adding the plugin. Add it as a `prebuild` step when CI invokes `next build` directly. If the server console suggests `payload generate:importmap`, run `frogbot generate:importmap` instead, or restart `frogbot dev`.
28
+ Build and start-only workflows must run `frogbot generate:importmap` after adding the plugin. Add it as a `prebuild` step when CI invokes `next build` directly. If the server console reports a component missing from the import map, run `frogbot generate:importmap` and rebuild, or restart `frogbot dev`.
29
29
 
30
30
  ```ts
31
31
  apiKeysPlugin({
@@ -1,10 +1,10 @@
1
- import type { CollectionConfig, FrogbotRequest } from 'frogbot';
1
+ import type { CollectionConfig, FrogBotRequest } from 'frogbot';
2
2
  type CollectionOptions = {
3
3
  authCollection: string;
4
4
  collectionSlug: string;
5
5
  tokenPrefix: string;
6
6
  usageCollection?: string;
7
- canRevokeAnyKey?: (req: FrogbotRequest) => boolean | Promise<boolean>;
7
+ canRevokeAnyKey?: (req: FrogBotRequest) => boolean | Promise<boolean>;
8
8
  collection?: Partial<CollectionConfig>;
9
9
  existing?: CollectionConfig;
10
10
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type CollectionConfig, type FrogbotRequest, type Plugin } from 'frogbot';
1
+ import { type CollectionConfig, type FrogBotRequest, type Plugin } from 'frogbot';
2
2
  export type { MintApiKeyOptions, RevokeApiKeyOptions, RotateApiKeyOptions, } from './server/services.js';
3
3
  export { ApiKeyServiceError, mintApiKey, revokeApiKey, rotateApiKey } from './server/services.js';
4
4
  export type { ApiKeyHeaderOptions, ApiKeyTokenOptions } from './server/token.js';
@@ -11,7 +11,7 @@ export type ApiKeysPluginOptions = {
11
11
  tokenPrefix?: string;
12
12
  headerNames?: string[];
13
13
  collection?: Partial<CollectionConfig>;
14
- canRevokeAnyKey?: (req: FrogbotRequest) => boolean | Promise<boolean>;
14
+ canRevokeAnyKey?: (req: FrogBotRequest) => boolean | Promise<boolean>;
15
15
  };
16
16
  export declare function apiKeysPlugin(options?: ApiKeysPluginOptions): Plugin;
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1,12 +1,12 @@
1
- import type { FrogbotRequest } from 'frogbot';
1
+ import type { FrogBotRequest } from 'frogbot';
2
2
  export type MintApiKeyOptions = {
3
- req: FrogbotRequest;
3
+ req: FrogBotRequest;
4
4
  collectionSlug: string;
5
5
  tokenPrefix: string;
6
6
  name: string;
7
7
  };
8
8
  export type RevokeApiKeyOptions = {
9
- req: FrogbotRequest;
9
+ req: FrogBotRequest;
10
10
  collectionSlug: string;
11
11
  id: string;
12
12
  anyOwner?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frogbotai/plugin-api-keys",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "Multiple named API keys for FrogBot.",
5
5
  "license": "MIT",
6
6
  "author": "Colby Gilbert",
@@ -38,8 +38,8 @@
38
38
  "@payloadcms/ui": "3.90.1",
39
39
  "react": "^19.0.1 || ^19.1.2 || ^19.2.1",
40
40
  "react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1",
41
- "@frogbotai/gateway": "0.25.0",
42
- "frogbot": "0.25.0"
41
+ "@frogbotai/gateway": "0.27.0",
42
+ "frogbot": "0.27.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@payloadcms/ui": "3.90.1",
@@ -48,15 +48,15 @@
48
48
  "react": "19.2.6",
49
49
  "react-dom": "19.2.6",
50
50
  "typescript": "5.6.2",
51
- "@frogbotai/gateway": "0.25.0",
52
- "frogbot": "0.25.0",
53
- "@frogbotai/plugin-roles": "0.25.0"
51
+ "frogbot": "0.27.0",
52
+ "@frogbotai/gateway": "0.27.0",
53
+ "@frogbotai/plugin-roles": "0.27.0"
54
54
  },
55
55
  "publishConfig": {
56
56
  "access": "public"
57
57
  },
58
58
  "scripts": {
59
- "build": "tsc -p tsconfig.json && cp src/client/styles.css dist/client/styles.css",
59
+ "build": "rm -rf dist && tsc -p tsconfig.json && cp src/client/styles.css dist/client/styles.css",
60
60
  "clean": "rm -rf dist",
61
61
  "typecheck": "tsc --noEmit"
62
62
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=ApiKeysManager.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ApiKeysManager.spec.d.ts","sourceRoot":"","sources":["../../src/client/ApiKeysManager.spec.tsx"],"names":[],"mappings":""}
@@ -1,109 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3
- import { beforeEach, describe, expect, it, vi } from 'vitest';
4
- const closeModal = vi.fn();
5
- const openModal = vi.fn();
6
- const clearRouteCache = vi.fn();
7
- vi.mock('@payloadcms/ui', () => ({
8
- Button: ({ children, ...props }) => (_jsx("button", { ...props, children: children })),
9
- CopyIcon: () => _jsx("svg", { "data-testid": "copy-icon" }),
10
- Modal: ({ children, className }) => (_jsx("div", { className: className, role: "dialog", children: children })),
11
- TextInput: ({ label, ...props }) => _jsx("input", { "aria-label": label, ...props }),
12
- Tooltip: ({ children, show }) => show ? _jsx("span", { children: children }) : null,
13
- useConfig: () => ({ config: { routes: { api: '/api' } } }),
14
- useListQuery: () => ({ collectionSlug: 'api-keys' }),
15
- useModal: () => ({ closeModal, openModal }),
16
- useRouteCache: () => ({ clearRouteCache }),
17
- }));
18
- const { ApiKeysManager, RevokeApiKey } = await import('./ApiKeysManager.js');
19
- describe('API key controls', () => {
20
- beforeEach(() => {
21
- vi.clearAllMocks();
22
- vi.stubGlobal('fetch', vi.fn());
23
- Object.assign(navigator, { clipboard: { writeText: vi.fn() } });
24
- });
25
- it('creates and reveals an API key in the modal', async () => {
26
- vi.mocked(fetch).mockResolvedValue({
27
- ok: true,
28
- json: () => Promise.resolve({ token: 'frogbot_secret' }),
29
- });
30
- render(_jsx(ApiKeysManager, {}));
31
- expect(screen.getByRole('dialog').className).toBe('api-keys-modal');
32
- fireEvent.click(screen.getByRole('button', { name: 'Create API key' }));
33
- expect(openModal).toHaveBeenCalledWith('create-api-key-modal');
34
- fireEvent.change(screen.getByLabelText('Key name'), { target: { value: 'Deploy' } });
35
- fireEvent.click(screen.getByRole('button', { name: 'Generate' }));
36
- expect(await screen.findByText("Key generated! Copy it now — you won't see it again.")).toBeTruthy();
37
- expect(screen.getByText('frogbot_secret')).toBeTruthy();
38
- expect(fetch).toHaveBeenCalledWith('/api/api-keys/mint', expect.objectContaining({
39
- body: JSON.stringify({ name: 'Deploy' }),
40
- method: 'POST',
41
- }));
42
- fireEvent.click(screen.getByRole('button', { name: 'Close' }));
43
- expect(closeModal).toHaveBeenCalledWith('create-api-key-modal');
44
- expect(clearRouteCache).toHaveBeenCalled();
45
- });
46
- it('generates on Enter in the key name input', async () => {
47
- vi.mocked(fetch).mockResolvedValue({
48
- ok: true,
49
- json: () => Promise.resolve({ token: 'frogbot_secret' }),
50
- });
51
- render(_jsx(ApiKeysManager, {}));
52
- fireEvent.change(screen.getByLabelText('Key name'), { target: { value: 'Deploy' } });
53
- fireEvent.keyDown(screen.getByLabelText('Key name'), { key: 'Enter' });
54
- expect(await screen.findByText('frogbot_secret')).toBeTruthy();
55
- });
56
- it('ignores Enter when the name is empty', () => {
57
- render(_jsx(ApiKeysManager, {}));
58
- fireEvent.keyDown(screen.getByLabelText('Key name'), { key: 'Enter' });
59
- expect(fetch).not.toHaveBeenCalled();
60
- });
61
- it('copies the token with feedback', async () => {
62
- vi.mocked(fetch).mockResolvedValue({
63
- ok: true,
64
- json: () => Promise.resolve({ token: 'frogbot_secret' }),
65
- });
66
- render(_jsx(ApiKeysManager, {}));
67
- fireEvent.change(screen.getByLabelText('Key name'), { target: { value: 'Deploy' } });
68
- fireEvent.click(screen.getByRole('button', { name: 'Generate' }));
69
- await screen.findByText('frogbot_secret');
70
- fireEvent.mouseEnter(screen.getByRole('button', { name: 'Copy API key' }));
71
- fireEvent.click(screen.getByRole('button', { name: 'Copy API key' }));
72
- await waitFor(() => expect(screen.getByText('Copied')).toBeTruthy());
73
- expect(navigator.clipboard.writeText).toHaveBeenCalledWith('frogbot_secret');
74
- expect(screen.getByTestId('copy-icon')).toBeTruthy();
75
- });
76
- it('does not clear the route cache when dismissed without a key', () => {
77
- render(_jsx(ApiKeysManager, {}));
78
- fireEvent.click(screen.getByRole('button', { name: 'Dismiss' }));
79
- expect(closeModal).toHaveBeenCalledWith('create-api-key-modal');
80
- expect(clearRouteCache).not.toHaveBeenCalled();
81
- });
82
- it('confirms before revoking an active key', async () => {
83
- vi.mocked(fetch).mockResolvedValue({ ok: true });
84
- render(_jsx(RevokeApiKey, { rowData: { id: 'key-1', name: 'Deploy' } }));
85
- fireEvent.click(screen.getByRole('button', { name: 'Revoke' }));
86
- expect(openModal).toHaveBeenCalledWith('revoke-api-key-modal-key-1');
87
- expect(screen.getByText('This will immediately disable Deploy. Any scripts or tools using this key will stop working. This cannot be undone.')).toBeTruthy();
88
- expect(fetch).not.toHaveBeenCalled();
89
- fireEvent.click(screen.getByRole('button', { name: 'Revoke key' }));
90
- await waitFor(() => expect(screen.getByText('Revoked')).toBeTruthy());
91
- expect(closeModal).toHaveBeenCalledWith('revoke-api-key-modal-key-1');
92
- expect(clearRouteCache).toHaveBeenCalled();
93
- expect(fetch).toHaveBeenCalledWith('/api/api-keys/key-1/revoke', { method: 'POST' });
94
- });
95
- it('cancels revocation without calling the endpoint', () => {
96
- render(_jsx(RevokeApiKey, { rowData: { id: 'key-1', name: 'Deploy' } }));
97
- fireEvent.click(screen.getByRole('button', { name: 'Revoke' }));
98
- fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
99
- expect(closeModal).toHaveBeenCalledWith('revoke-api-key-modal-key-1');
100
- expect(fetch).not.toHaveBeenCalled();
101
- expect(screen.getByRole('button', { name: 'Revoke' })).toBeTruthy();
102
- });
103
- it('shows revoked keys without an action', () => {
104
- render(_jsx(RevokeApiKey, { rowData: { id: 'key-1', revokedAt: '2026-07-29' } }));
105
- expect(screen.getByText('Revoked')).toBeTruthy();
106
- expect(screen.queryByRole('button', { name: 'Revoke' })).toBeNull();
107
- });
108
- });
109
- //# sourceMappingURL=ApiKeysManager.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ApiKeysManager.spec.js","sourceRoot":"","sources":["../../src/client/ApiKeysManager.spec.tsx"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=CostUSD.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CostUSD.spec.d.ts","sourceRoot":"","sources":["../../src/client/CostUSD.spec.tsx"],"names":[],"mappings":""}
@@ -1,19 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { render, screen } from '@testing-library/react';
3
- import { describe, expect, it, vi } from 'vitest';
4
- vi.mock('@payloadcms/ui', () => ({
5
- FieldLabel: () => null,
6
- useField: () => ({ value: 0 }),
7
- }));
8
- const { CostUSDCell, formatCostUSD } = await import('./CostUSD.js');
9
- describe('USD cost formatting', () => {
10
- it('formats dollars with sub-cent precision', () => {
11
- expect(formatCostUSD(12.5)).toBe('$12.50');
12
- expect(formatCostUSD(0.000322618)).toBe('$0.000323');
13
- });
14
- it('renders formatted list cells', () => {
15
- render(_jsx(CostUSDCell, { cellData: 1.2345678 }));
16
- expect(screen.getByText('$1.234568')).toBeTruthy();
17
- });
18
- });
19
- //# sourceMappingURL=CostUSD.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CostUSD.spec.js","sourceRoot":"","sources":["../../src/client/CostUSD.spec.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/B,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI;IACtB,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;CAC/B,CAAC,CAAC,CAAC;AAEJ,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;AAEpE,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,KAAC,WAAW,IAAC,QAAQ,EAAE,SAAS,GAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/fields.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import type { Field } from 'frogbot';
2
- export declare function createPolicyFields(modelIDs: string[]): Field[];
3
- //# sourceMappingURL=fields.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAa9D"}
package/dist/fields.js DELETED
@@ -1,15 +0,0 @@
1
- export function createPolicyFields(modelIDs) {
2
- return [
3
- { name: 'monthlyBudget', label: 'Monthly Budget (USD)', type: 'number', min: 0 },
4
- { name: 'models', type: 'select', hasMany: true, options: modelIDs },
5
- {
6
- name: 'spendThisPeriodUSD',
7
- label: 'Monthly Spend (USD)',
8
- type: 'number',
9
- defaultValue: 0,
10
- access: { update: () => false },
11
- admin: { readOnly: true },
12
- },
13
- ];
14
- }
15
- //# sourceMappingURL=fields.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,OAAO;QACL,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,sBAAsB,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE;QAChF,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE;QACpE;YACE,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,qBAAqB;YAC5B,IAAI,EAAE,QAAQ;YACd,YAAY,EAAE,CAAC;YACf,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE;YAC/B,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC1B;KACF,CAAC;AACJ,CAAC"}
package/dist/policy.d.ts DELETED
@@ -1,11 +0,0 @@
1
- export type PolicyDocument = {
2
- apiKeyId?: string;
3
- monthlyBudget?: number;
4
- models?: string[];
5
- spendThisPeriodUSD?: number;
6
- };
7
- export declare class SerialQueue {
8
- private readonly updates;
9
- run(subject: string, update: () => Promise<void>): Promise<void>;
10
- }
11
- //# sourceMappingURL=policy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAE5D,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAUjE"}
package/dist/policy.js DELETED
@@ -1,15 +0,0 @@
1
- export class SerialQueue {
2
- updates = new Map();
3
- run(subject, update) {
4
- const next = (this.updates.get(subject) ?? Promise.resolve()).then(update, update);
5
- let owner;
6
- const release = () => {
7
- if (this.updates.get(subject) === owner)
8
- this.updates.delete(subject);
9
- };
10
- owner = next.then(release, release);
11
- this.updates.set(subject, owner);
12
- return next;
13
- }
14
- }
15
- //# sourceMappingURL=policy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,WAAW;IACL,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE5D,GAAG,CAAC,OAAe,EAAE,MAA2B;QAC9C,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnF,IAAI,KAAoB,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxE,CAAC,CAAC;QACF,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}