@frogbotai/plugin-api-keys 0.28.0 → 0.30.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
@@ -17,13 +17,13 @@ export default buildConfig({
17
17
  The plugin adds an `api-keys` collection, owner-scoped management endpoints, API-key authentication, and default admin controls. `frogbot dev` generates the admin import map before starting, so the key-creation controls appear on the first load. Create a key in the API Keys admin collection, then authenticate requests with either header:
18
18
 
19
19
  ```http
20
- Authorization: Bearer fbt_...
21
- X-API-Key: fbt_...
20
+ Authorization: Bearer fb_...
21
+ X-API-Key: fb_...
22
22
  ```
23
23
 
24
24
  Plaintext keys are returned only when created. The collection stores SHA-256 hashes and supports multiple independently revoked keys per user.
25
25
 
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`.
26
+ A key has no budget or model limits of its own; requests use the owner's AI policy. 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
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
 
@@ -40,15 +40,14 @@ apiKeysPlugin({
40
40
  });
41
41
  ```
42
42
 
43
- Collection overrides merge with the generated fields, access, endpoints, hooks, admin components, and transforms applied by other plugins. The manager uses Payload controls without plugin CSS, so global admin styles continue to apply.
43
+ Collection overrides merge with the generated fields, access, endpoints, hooks, admin components, and transforms applied by other plugins. The manager uses the default admin components, and the token dialog ships a small stylesheet built on the admin theme variables.
44
44
 
45
- The plugin has no role system of its own. Keys are owner-scoped, and budget fields are read-only. To layer roles on top, install `@frogbotai/plugin-roles`, list it after `apiKeysPlugin()`, and pass `policyAccess` and `canRevokeAnyKey`:
45
+ The plugin has no role system of its own. Keys are owner-scoped. To layer roles on top, install `@frogbotai/plugin-roles`, list it after `apiKeysPlugin()`, and pass `canRevokeAnyKey` and collection access:
46
46
 
47
47
  ```ts
48
48
  import { allow, hasRole } from '@frogbotai/plugin-roles';
49
49
 
50
50
  apiKeysPlugin({
51
- policyAccess: allow('admin'),
52
51
  canRevokeAnyKey: (req) => hasRole(req, 'admin', 'support'),
53
52
  collection: {
54
53
  access: {
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import './styles.css';
4
- import { Button, CopyIcon, Modal, TextInput, Tooltip, useConfig, useListQuery, useModal, useRouteCache, } from '@payloadcms/ui';
4
+ import { Button, CopyIcon, Modal, TextInput, Tooltip, useConfig, useListQuery, useModal, useRouteCache } from '@payloadcms/ui';
5
5
  import { useState } from 'react';
6
6
  const modalSlug = 'create-api-key-modal';
7
7
  export function ApiKeysManager() {
@@ -24,20 +24,22 @@ export function ApiKeysManager() {
24
24
  }
25
25
  function close() {
26
26
  closeModal(modalSlug);
27
- if (token)
28
- clearRouteCache();
27
+ if (token) clearRouteCache();
29
28
  }
30
29
  async function createKey() {
31
- if (loading || !name.trim())
32
- return;
30
+ if (loading || !name.trim()) return;
33
31
  setLoading(true);
34
32
  setError(undefined);
35
33
  const response = await fetch(`${config.routes.api}/${collectionSlug}/mint`, {
36
34
  method: 'POST',
37
- headers: { 'content-type': 'application/json' },
38
- body: JSON.stringify({ name }),
35
+ headers: {
36
+ 'content-type': 'application/json'
37
+ },
38
+ body: JSON.stringify({
39
+ name
40
+ })
39
41
  });
40
- const result = (await response.json());
42
+ const result = await response.json();
41
43
  setLoading(false);
42
44
  if (!response.ok || !result.token) {
43
45
  setError(result.error ?? 'Unable to create API key');
@@ -46,32 +48,133 @@ export function ApiKeysManager() {
46
48
  setToken(result.token);
47
49
  }
48
50
  async function copyToken() {
49
- if (!token)
50
- return;
51
+ if (!token) return;
51
52
  await navigator.clipboard.writeText(token);
52
53
  setCopied(true);
53
54
  }
54
55
  function copyHandlers(control) {
55
56
  return {
56
57
  onClick: copyToken,
57
- onMouseEnter: () => {
58
+ onMouseEnter: ()=>{
58
59
  setHoveredControl(control);
59
60
  setCopied(false);
60
61
  },
61
- onMouseLeave: () => {
62
+ onMouseLeave: ()=>{
62
63
  setHoveredControl(undefined);
63
64
  setCopied(false);
64
- },
65
+ }
65
66
  };
66
67
  }
67
- return (_jsxs(_Fragment, { children: [_jsx(Button, { buttonStyle: "secondary", margin: false, onClick: open, type: "button", children: "Create API key" }), _jsx(Modal, { className: "api-keys-modal", closeOnBlur: true, onClose: close, slug: modalSlug, children: _jsxs("div", { className: "api-keys-modal__wrapper", children: [_jsxs("div", { className: "api-keys-modal__content", children: [_jsx("h1", { children: "Create API Key" }), token ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "api-keys-modal__warning", role: "alert", children: "Key generated! Copy it now \u2014 you won't see it again." }), _jsxs("div", { className: "api-keys-modal__token-row", children: [_jsxs("button", { "aria-label": "Click to copy API key", className: "api-keys-clear-button api-keys-modal__token", type: "button", ...copyHandlers('token'), children: [_jsx("code", { children: token }), _jsx(Tooltip, { delay: copied ? 0 : undefined, show: hoveredControl === 'token', children: copied ? 'Copied' : 'Click to copy' })] }), _jsxs("button", { "aria-label": "Copy API key", className: "api-keys-clear-button api-keys-slide-up-1 api-keys-modal__copy", type: "button", ...copyHandlers('icon'), children: [_jsx(CopyIcon, {}), _jsx(Tooltip, { delay: copied ? 0 : undefined, show: hoveredControl === 'icon', children: copied ? 'Copied' : 'Copy' })] })] })] })) : (_jsx(TextInput, { label: "Key name", path: "api-key-name", placeholder: "Key name (e.g. My MacBook CLI)", readOnly: loading, value: name, onChange: (event) => setName(event.target.value), onKeyDown: (event) => {
68
+ return /*#__PURE__*/ _jsxs(_Fragment, {
69
+ children: [
70
+ /*#__PURE__*/ _jsx(Button, {
71
+ buttonStyle: "secondary",
72
+ margin: false,
73
+ onClick: open,
74
+ type: "button",
75
+ children: "Create API key"
76
+ }),
77
+ /*#__PURE__*/ _jsx(Modal, {
78
+ className: "api-keys-modal",
79
+ closeOnBlur: true,
80
+ onClose: close,
81
+ slug: modalSlug,
82
+ children: /*#__PURE__*/ _jsxs("div", {
83
+ className: "api-keys-modal__wrapper",
84
+ children: [
85
+ /*#__PURE__*/ _jsxs("div", {
86
+ className: "api-keys-modal__content",
87
+ children: [
88
+ /*#__PURE__*/ _jsx("h1", {
89
+ children: "Create API Key"
90
+ }),
91
+ token ? /*#__PURE__*/ _jsxs(_Fragment, {
92
+ children: [
93
+ /*#__PURE__*/ _jsx("p", {
94
+ className: "api-keys-modal__warning",
95
+ role: "alert",
96
+ children: "Key generated! Copy it now — you won't see it again."
97
+ }),
98
+ /*#__PURE__*/ _jsxs("div", {
99
+ className: "api-keys-modal__token-row",
100
+ children: [
101
+ /*#__PURE__*/ _jsxs("button", {
102
+ "aria-label": "Click to copy API key",
103
+ className: "api-keys-clear-button api-keys-modal__token",
104
+ type: "button",
105
+ ...copyHandlers('token'),
106
+ children: [
107
+ /*#__PURE__*/ _jsx("code", {
108
+ children: token
109
+ }),
110
+ /*#__PURE__*/ _jsx(Tooltip, {
111
+ delay: copied ? 0 : undefined,
112
+ show: hoveredControl === 'token',
113
+ children: copied ? 'Copied' : 'Click to copy'
114
+ })
115
+ ]
116
+ }),
117
+ /*#__PURE__*/ _jsxs("button", {
118
+ "aria-label": "Copy API key",
119
+ className: "api-keys-clear-button api-keys-slide-up-1 api-keys-modal__copy",
120
+ type: "button",
121
+ ...copyHandlers('icon'),
122
+ children: [
123
+ /*#__PURE__*/ _jsx(CopyIcon, {}),
124
+ /*#__PURE__*/ _jsx(Tooltip, {
125
+ delay: copied ? 0 : undefined,
126
+ show: hoveredControl === 'icon',
127
+ children: copied ? 'Copied' : 'Copy'
128
+ })
129
+ ]
130
+ })
131
+ ]
132
+ })
133
+ ]
134
+ }) : /*#__PURE__*/ _jsx(TextInput, {
135
+ label: "Key name",
136
+ path: "api-key-name",
137
+ placeholder: "Key name (e.g. My MacBook CLI)",
138
+ readOnly: loading,
139
+ value: name,
140
+ onChange: (event)=>setName(event.target.value),
141
+ onKeyDown: (event)=>{
68
142
  if (event.key === 'Enter') {
69
143
  event.preventDefault();
70
144
  void createKey();
71
145
  }
72
- } })), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs("div", { className: "api-keys-modal__controls", children: [_jsx(Button, { buttonStyle: "secondary", onClick: close, type: "button", children: token ? 'Close' : 'Dismiss' }), token ? null : (_jsx(Button, { disabled: loading || !name.trim(), onClick: createKey, type: "button", children: loading ? 'Generating…' : 'Generate' }))] })] }) })] }));
146
+ }
147
+ }),
148
+ error ? /*#__PURE__*/ _jsx("p", {
149
+ role: "alert",
150
+ children: error
151
+ }) : null
152
+ ]
153
+ }),
154
+ /*#__PURE__*/ _jsxs("div", {
155
+ className: "api-keys-modal__controls",
156
+ children: [
157
+ /*#__PURE__*/ _jsx(Button, {
158
+ buttonStyle: "secondary",
159
+ onClick: close,
160
+ type: "button",
161
+ children: token ? 'Close' : 'Dismiss'
162
+ }),
163
+ token ? null : /*#__PURE__*/ _jsx(Button, {
164
+ disabled: loading || !name.trim(),
165
+ onClick: createKey,
166
+ type: "button",
167
+ children: loading ? 'Generating…' : 'Generate'
168
+ })
169
+ ]
170
+ })
171
+ ]
172
+ })
173
+ })
174
+ ]
175
+ });
73
176
  }
74
- export function RevokeApiKey({ rowData, }) {
177
+ export function RevokeApiKey({ rowData }) {
75
178
  const { config } = useConfig();
76
179
  const { collectionSlug } = useListQuery();
77
180
  const { closeModal, openModal } = useModal();
@@ -84,11 +187,11 @@ export function RevokeApiKey({ rowData, }) {
84
187
  setLoading(true);
85
188
  setError(undefined);
86
189
  const response = await fetch(`${config.routes.api}/${collectionSlug}/${rowData.id}/revoke`, {
87
- method: 'POST',
190
+ method: 'POST'
88
191
  });
89
192
  setLoading(false);
90
193
  if (!response.ok) {
91
- const result = (await response.json());
194
+ const result = await response.json();
92
195
  setError(result.error ?? 'Unable to revoke API key');
93
196
  return;
94
197
  }
@@ -96,8 +199,68 @@ export function RevokeApiKey({ rowData, }) {
96
199
  setRevoked(true);
97
200
  clearRouteCache();
98
201
  }
99
- if (revoked || rowData.revokedAt)
100
- return _jsx("span", { children: "Revoked" });
101
- return (_jsxs(_Fragment, { children: [_jsx(Button, { margin: false, buttonStyle: "secondary", onClick: () => openModal(confirmSlug), size: "small", type: "button", children: "Revoke" }), _jsx(Modal, { className: "api-keys-modal", closeOnBlur: true, onClose: () => closeModal(confirmSlug), slug: confirmSlug, children: _jsxs("div", { className: "api-keys-modal__wrapper", children: [_jsxs("div", { className: "api-keys-modal__content", children: [_jsx("h1", { children: "Revoke API Key" }), _jsxs("p", { children: ["This will immediately disable ", rowData.name ?? 'this key', ". Any scripts or tools using this key will stop working. This cannot be undone."] }), error ? _jsx("p", { role: "alert", children: error }) : null] }), _jsxs("div", { className: "api-keys-modal__controls", children: [_jsx(Button, { buttonStyle: "secondary", onClick: () => closeModal(confirmSlug), type: "button", children: "Cancel" }), _jsx(Button, { disabled: loading, onClick: revoke, type: "button", children: loading ? 'Revoking…' : 'Revoke key' })] })] }) })] }));
202
+ if (revoked || rowData.revokedAt) return /*#__PURE__*/ _jsx("span", {
203
+ children: "Revoked"
204
+ });
205
+ return /*#__PURE__*/ _jsxs(_Fragment, {
206
+ children: [
207
+ /*#__PURE__*/ _jsx(Button, {
208
+ margin: false,
209
+ buttonStyle: "secondary",
210
+ onClick: ()=>openModal(confirmSlug),
211
+ size: "small",
212
+ type: "button",
213
+ children: "Revoke"
214
+ }),
215
+ /*#__PURE__*/ _jsx(Modal, {
216
+ className: "api-keys-modal",
217
+ closeOnBlur: true,
218
+ onClose: ()=>closeModal(confirmSlug),
219
+ slug: confirmSlug,
220
+ children: /*#__PURE__*/ _jsxs("div", {
221
+ className: "api-keys-modal__wrapper",
222
+ children: [
223
+ /*#__PURE__*/ _jsxs("div", {
224
+ className: "api-keys-modal__content",
225
+ children: [
226
+ /*#__PURE__*/ _jsx("h1", {
227
+ children: "Revoke API Key"
228
+ }),
229
+ /*#__PURE__*/ _jsxs("p", {
230
+ children: [
231
+ "This will immediately disable ",
232
+ rowData.name ?? 'this key',
233
+ ". Any scripts or tools using this key will stop working. This cannot be undone."
234
+ ]
235
+ }),
236
+ error ? /*#__PURE__*/ _jsx("p", {
237
+ role: "alert",
238
+ children: error
239
+ }) : null
240
+ ]
241
+ }),
242
+ /*#__PURE__*/ _jsxs("div", {
243
+ className: "api-keys-modal__controls",
244
+ children: [
245
+ /*#__PURE__*/ _jsx(Button, {
246
+ buttonStyle: "secondary",
247
+ onClick: ()=>closeModal(confirmSlug),
248
+ type: "button",
249
+ children: "Cancel"
250
+ }),
251
+ /*#__PURE__*/ _jsx(Button, {
252
+ disabled: loading,
253
+ onClick: revoke,
254
+ type: "button",
255
+ children: loading ? 'Revoking…' : 'Revoke key'
256
+ })
257
+ ]
258
+ })
259
+ ]
260
+ })
261
+ })
262
+ ]
263
+ });
102
264
  }
265
+
103
266
  //# sourceMappingURL=ApiKeysManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeysManager.js","sourceRoot":"","sources":["../../src/client/ApiKeysManager.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/client/ApiKeysManager.tsx"],"names":["Button","CopyIcon","Modal","TextInput","Tooltip","useConfig","useListQuery","useModal","useRouteCache","useState","modalSlug","ApiKeysManager","config","collectionSlug","closeModal","openModal","clearRouteCache","name","setName","token","setToken","error","setError","loading","setLoading","copied","setCopied","hoveredControl","setHoveredControl","open","undefined","close","createKey","trim","response","fetch","routes","api","method","headers","body","JSON","stringify","result","json","ok","copyToken","navigator","clipboard","writeText","copyHandlers","control","onClick","onMouseEnter","onMouseLeave","buttonStyle","margin","type","className","closeOnBlur","onClose","slug","div","h1","p","role","button","aria-label","code","delay","show","label","path","placeholder","readOnly","value","onChange","event","target","onKeyDown","key","preventDefault","disabled","RevokeApiKey","rowData","revoked","setRevoked","confirmSlug","id","revoke","revokedAt","span","size"],"mappings":"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","file":"ApiKeysManager.js"}
@@ -1,89 +1,91 @@
1
- .api-keys-modal {
2
- align-items: center;
3
- backdrop-filter: blur(8px);
4
- background: color-mix(in srgb, var(--theme-elevation-0) 80%, transparent);
5
- display: flex;
6
- height: 100%;
7
- justify-content: center;
8
- }
1
+ @layer frogbot {
2
+ .api-keys-modal {
3
+ align-items: center;
4
+ backdrop-filter: blur(8px);
5
+ background: color-mix(in srgb, var(--theme-elevation-0) 80%, transparent);
6
+ display: flex;
7
+ height: 100%;
8
+ justify-content: center;
9
+ }
9
10
 
10
- .api-keys-modal__wrapper {
11
- background: var(--theme-elevation-50);
12
- border: 1px solid var(--theme-elevation-150);
13
- border-radius: var(--style-radius-m);
14
- display: flex;
15
- flex-direction: column;
16
- gap: 24px;
17
- max-width: 720px;
18
- padding: 32px;
19
- position: relative;
20
- width: min(90vw, 720px);
21
- z-index: 1;
22
- }
11
+ .api-keys-modal__wrapper {
12
+ background: var(--theme-elevation-50);
13
+ border: 1px solid var(--theme-elevation-150);
14
+ border-radius: var(--style-radius-m);
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 24px;
18
+ max-width: 720px;
19
+ padding: 32px;
20
+ position: relative;
21
+ width: min(90vw, 720px);
22
+ z-index: 1;
23
+ }
23
24
 
24
- .api-keys-modal__content {
25
- display: flex;
26
- flex-direction: column;
27
- gap: 12px;
28
- }
25
+ .api-keys-modal__content {
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: 12px;
29
+ }
29
30
 
30
- .api-keys-modal__content > * {
31
- margin: 0;
32
- }
31
+ .api-keys-modal__content > * {
32
+ margin: 0;
33
+ }
33
34
 
34
- .api-keys-modal__warning {
35
- color: var(--theme-success-500);
36
- font-weight: 600;
37
- }
35
+ .api-keys-modal__warning {
36
+ color: var(--theme-success-500);
37
+ font-weight: 600;
38
+ }
38
39
 
39
- .api-keys-modal__token-row {
40
- align-items: center;
41
- background: var(--theme-elevation-0);
42
- border: 1px solid var(--theme-elevation-150);
43
- border-radius: var(--style-radius-m);
44
- display: flex;
45
- gap: 8px;
46
- padding: 4px 4px 4px 16px;
47
- transition: border-color 200ms ease-out;
48
- }
40
+ .api-keys-modal__token-row {
41
+ align-items: center;
42
+ background: var(--theme-elevation-0);
43
+ border: 1px solid var(--theme-elevation-150);
44
+ border-radius: var(--style-radius-m);
45
+ display: flex;
46
+ gap: 8px;
47
+ padding: 4px 4px 4px 16px;
48
+ transition: border-color 200ms ease-out;
49
+ }
49
50
 
50
- .api-keys-modal__token-row:hover {
51
- border-color: var(--theme-elevation-400);
52
- }
51
+ .api-keys-modal__token-row:hover {
52
+ border-color: var(--theme-elevation-400);
53
+ }
53
54
 
54
- .api-keys-modal__token {
55
- flex: 1;
56
- overflow-wrap: anywhere;
57
- padding: 8px 0;
58
- position: relative;
59
- text-align: left;
60
- }
55
+ .api-keys-modal__token {
56
+ flex: 1;
57
+ overflow-wrap: anywhere;
58
+ padding: 8px 0;
59
+ position: relative;
60
+ text-align: left;
61
+ }
61
62
 
62
- .api-keys-modal__copy {
63
- padding: 9px;
64
- position: relative;
65
- }
63
+ .api-keys-modal__copy {
64
+ padding: 9px;
65
+ position: relative;
66
+ }
66
67
 
67
- .api-keys-modal__controls {
68
- display: flex;
69
- gap: 12px;
70
- }
68
+ .api-keys-modal__controls {
69
+ display: flex;
70
+ gap: 12px;
71
+ }
71
72
 
72
- .api-keys-clear-button {
73
- background: none;
74
- border: none;
75
- box-shadow: none;
76
- box-sizing: border-box;
77
- cursor: pointer;
78
- line-height: 1;
79
- margin: 0;
80
- outline: none;
81
- }
73
+ .api-keys-clear-button {
74
+ background: none;
75
+ border: none;
76
+ box-shadow: none;
77
+ box-sizing: border-box;
78
+ cursor: pointer;
79
+ line-height: 1;
80
+ margin: 0;
81
+ outline: none;
82
+ }
82
83
 
83
- .api-keys-slide-up-1 {
84
- transition: transform 200ms ease-out;
85
- }
84
+ .api-keys-slide-up-1 {
85
+ transition: transform 200ms ease-out;
86
+ }
86
87
 
87
- .api-keys-slide-up-1:hover {
88
- transform: translateY(-4px);
88
+ .api-keys-slide-up-1:hover {
89
+ transform: translateY(-4px);
90
+ }
89
91
  }
package/dist/client.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export { ApiKeysManager, RevokeApiKey } from './client/ApiKeysManager.js';
2
- export { CostUSDCell, CostUSDField } from './client/CostUSD.js';
3
2
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/client.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { ApiKeysManager, RevokeApiKey } from './client/ApiKeysManager.js';
2
- export { CostUSDCell, CostUSDField } from './client/CostUSD.js';
2
+
3
3
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"sources":["../src/client.ts"],"names":["ApiKeysManager","RevokeApiKey"],"mappings":"AAAA,SAASA,cAAc,EAAEC,YAAY,QAAQ,6BAA6B","file":"client.js"}
@@ -1 +1 @@
1
- {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAIhB,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB,KAAK,iBAAiB,GAAG;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAoIF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAqHpF"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAIhB,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB,KAAK,iBAAiB,GAAG;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAoIF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAsHpF"}