@unkey/api 0.12.1 → 0.13.1

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/dist/index.js CHANGED
@@ -25,15 +25,23 @@ __export(src_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(src_exports);
27
27
 
28
+ // package.json
29
+ var version = "0.13.1";
30
+
28
31
  // src/client.ts
29
32
  var Unkey = class {
30
33
  baseUrl;
31
34
  rootKey;
32
35
  cache;
36
+ sdkVersions = [];
33
37
  retry;
34
38
  constructor(opts) {
35
39
  this.baseUrl = opts.baseUrl ?? "https://api.unkey.dev";
36
40
  this.rootKey = opts.rootKey ?? opts.token;
41
+ this.sdkVersions.push(`v${version}`);
42
+ if (opts.wrapperSdkVersion) {
43
+ this.sdkVersions.push(opts.wrapperSdkVersion);
44
+ }
37
45
  this.cache = opts.cache;
38
46
  if (!this.rootKey) {
39
47
  throw new Error(
@@ -52,14 +60,15 @@ var Unkey = class {
52
60
  const url = new URL(`${this.baseUrl}/${req.path.join("/")}`);
53
61
  if (req.query) {
54
62
  for (const [k, v] of Object.entries(req.query)) {
55
- url.searchParams.set(k, v);
63
+ url.searchParams.set(k, v.toString());
56
64
  }
57
65
  }
58
66
  res = await fetch(url, {
59
67
  method: req.method,
60
68
  headers: {
61
69
  "Content-Type": "application/json",
62
- Authorization: `Bearer ${this.rootKey}`
70
+ Authorization: `Bearer ${this.rootKey}`,
71
+ "Unkey-SDK": this.sdkVersions.join(",")
63
72
  },
64
73
  cache: this.cache,
65
74
  body: JSON.stringify(req.body)
@@ -87,6 +96,7 @@ var Unkey = class {
87
96
  }
88
97
  return {
89
98
  error: {
99
+ // @ts-ignore
90
100
  code: "FETCH_ERROR",
91
101
  // @ts-ignore I don't understand why `err` is `never`
92
102
  message: err?.message ?? "No response",
@@ -99,92 +109,77 @@ var Unkey = class {
99
109
  return {
100
110
  create: async (req) => {
101
111
  return await this.fetch({
102
- path: ["v1", "keys"],
112
+ path: ["v1", "keys.createKey"],
103
113
  method: "POST",
104
114
  body: req
105
115
  });
106
116
  },
107
117
  update: async (req) => {
108
118
  return await this.fetch({
109
- path: ["v1", "keys", req.keyId],
110
- method: "PUT",
119
+ path: ["v1", "keys.updateKey"],
120
+ method: "POST",
111
121
  body: req
112
122
  });
113
123
  },
114
124
  verify: async (req) => {
115
125
  return await this.fetch({
116
- path: ["v1", "keys", "verify"],
126
+ path: ["v1", "keys.verifyKey"],
117
127
  method: "POST",
118
128
  body: req
119
129
  });
120
130
  },
121
- revoke: async (req) => {
122
- return await this.fetch({
123
- path: ["v1", "keys", req.keyId],
124
- method: "DELETE"
125
- });
126
- }
127
- };
128
- }
129
- get apis() {
130
- return {
131
- create: async (req) => {
131
+ delete: async (req) => {
132
132
  return await this.fetch({
133
- path: ["v1", "apis.createApi"],
133
+ path: ["v1", "keys.deleteKey"],
134
134
  method: "POST",
135
135
  body: req
136
136
  });
137
137
  },
138
- remove: async (req) => {
138
+ updateRemaining: async (req) => {
139
139
  return await this.fetch({
140
- path: ["v1", "apis.removeApi"],
140
+ path: ["v1", "keys.updateRemaining"],
141
141
  method: "POST",
142
142
  body: req
143
143
  });
144
144
  },
145
145
  get: async (req) => {
146
146
  return await this.fetch({
147
- path: ["v1", "apis", req.apiId],
148
- method: "GET"
149
- });
150
- },
151
- listKeys: async (req) => {
152
- const query = {};
153
- if (typeof req.limit !== "undefined") {
154
- query.limit = req.limit.toString();
155
- }
156
- if (typeof req.offset !== "undefined") {
157
- query.offset = req.offset.toString();
158
- }
159
- if (typeof req.ownerId !== "undefined") {
160
- query.ownerId = req.ownerId;
161
- }
162
- return await this.fetch({
163
- path: ["v1", "apis", req.apiId, "keys"],
147
+ path: ["v1", "keys.getKey"],
164
148
  method: "GET",
165
- query
149
+ query: req
166
150
  });
167
151
  }
168
152
  };
169
153
  }
170
- /**
171
- * Must be authenticated via app token
172
- */
173
- get _internal() {
154
+ get apis() {
174
155
  return {
175
- createRootKey: async (req) => {
156
+ create: async (req) => {
176
157
  return await this.fetch({
177
- path: ["v1", "internal", "rootkeys"],
158
+ path: ["v1", "apis.createApi"],
178
159
  method: "POST",
179
160
  body: req
180
161
  });
181
162
  },
182
- deleteRootKey: async (req) => {
163
+ delete: async (req) => {
183
164
  return await this.fetch({
184
- path: ["v1", "internal.removeRootKey"],
165
+ path: ["v1", "apis.deleteApi"],
185
166
  method: "POST",
186
167
  body: req
187
168
  });
169
+ },
170
+ get: async (req) => {
171
+ return await this.fetch({
172
+ path: ["v1", "apis.getApi"],
173
+ method: "GET",
174
+ query: req
175
+ });
176
+ },
177
+ listKeys: async (req) => {
178
+ return await this.fetch({
179
+ path: ["v1", "apis.listKeys"],
180
+ method: "GET",
181
+ query: req
182
+ });
188
183
  }
189
184
  };
190
185
  }
@@ -192,7 +187,7 @@ var Unkey = class {
192
187
 
193
188
  // src/verify.ts
194
189
  function verifyKey(req) {
195
- const unkey = new Unkey({ token: "public" });
190
+ const unkey = new Unkey({ rootKey: "public" });
196
191
  return unkey.keys.verify(typeof req === "string" ? { key: req } : req);
197
192
  }
198
193
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/verify.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./verify\";\nexport * from \"./errors\";\n","import { UnkeyError } from \"./errors\";\n\nexport type UnkeyOptions = (\n | {\n token?: never;\n\n /**\n * The root key from unkey.dev.\n *\n * You can create/manage your root keys here:\n * https://unkey.dev/app/settings/root-keys\n */\n rootKey: string;\n }\n | {\n /**\n * The workspace key from unkey.dev\n *\n * @deprecated Use `rootKey`\n */\n token: string;\n rootKey?: never;\n }\n) & {\n /**\n * @default https://api.unkey.dev\n */\n baseUrl?: string;\n\n /**\n * Retry on network errors\n */\n retry?: {\n /**\n * How many attempts should be made\n * The maximum number of requests will be `attempts + 1`\n * `0` means no retries\n *\n * @default 5\n */\n attempts?: number;\n /**\n * Return how many milliseconds to wait until the next attempt is made\n *\n * @default `(retryCount) => Math.round(Math.exp(retryCount) * 10)),`\n */\n backoff?: (retryCount: number) => number;\n };\n /**\n * Customize the `fetch` cache behaviour\n */\n cache?: RequestCache;\n // some change\n};\n\ntype ApiRequest = {\n path: string[];\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n query?: Record<string, string>;\n};\n\ntype Result<R> =\n | {\n result: R;\n error?: never;\n }\n | {\n result?: never;\n error: UnkeyError;\n };\n\nexport class Unkey {\n public readonly baseUrl: string;\n private readonly rootKey: string;\n private readonly cache?: RequestCache;\n\n public readonly retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n constructor(opts: UnkeyOptions) {\n this.baseUrl = opts.baseUrl ?? \"https://api.unkey.dev\";\n this.rootKey = opts.rootKey ?? opts.token;\n\n this.cache = opts.cache;\n /**\n * Even though typescript should prevent this, some people still pass undefined or empty strings\n */\n if (!this.rootKey) {\n throw new Error(\n \"Unkey root key must be set, maybe you passed in `undefined` or an empty string?\",\n );\n }\n\n this.retry = {\n attempts: opts.retry?.attempts ?? 5,\n backoff: opts.retry?.backoff ?? ((n) => Math.round(Math.exp(n) * 10)),\n };\n }\n\n private async fetch<TResult>(req: ApiRequest): Promise<Result<TResult>> {\n let res: Response | null = null;\n let err: Error | null = null;\n for (let i = 0; i <= this.retry.attempts; i++) {\n const url = new URL(`${this.baseUrl}/${req.path.join(\"/\")}`);\n if (req.query) {\n for (const [k, v] of Object.entries(req.query)) {\n url.searchParams.set(k, v);\n }\n }\n res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.rootKey}`,\n },\n cache: this.cache,\n body: JSON.stringify(req.body),\n }).catch((e: Error) => {\n err = e;\n return null; // set `res` to `null`\n });\n if (res?.ok) {\n return { result: (await res.json()) as TResult };\n }\n const backoff = this.retry.backoff(i);\n console.debug(\n \"attempt %d of %d to reach %s failed, retrying in %d ms: %s\",\n i + 1,\n this.retry.attempts + 1,\n url,\n backoff,\n // @ts-ignore I don't understand why `err` is `never`\n err?.message,\n );\n await new Promise((r) => setTimeout(r, backoff));\n }\n\n if (res) {\n return { error: (await res.json()) as UnkeyError };\n }\n\n return {\n error: {\n code: \"FETCH_ERROR\",\n // @ts-ignore I don't understand why `err` is `never`\n message: err?.message ?? \"No response\",\n docs: \"https://developer.mozilla.org/en-US/docs/Web/API/fetch\",\n requestId: \"N/A\",\n },\n };\n }\n\n public get keys() {\n return {\n create: async (req: {\n /**\n * Provide a name to this key if you want for later reference\n */\n name?: string;\n /**\n * Choose an API where this key should be created.\n */\n apiId: string;\n /**\n * To make it easier for your users to understand which product an api key belongs to, you can add prefix them.\n *\n * For example Stripe famously prefixes their customer ids with cus_ or their api keys with sk_live_.\n *\n * The underscore is automtically added if you are defining a prefix, for example: \"prefix\": \"abc\" will result in a key like abc_xxxxxxxxx\n */\n prefix?: string;\n\n /**\n * The bytelength used to generate your key determines its entropy as well as its length. Higher is better, but keys become longer and more annoying to handle.\n *\n * The default is 16 bytes, or 2128 possible combinations\n */\n byteLength?: number;\n /**\n * Your user’s Id. This will provide a link between Unkey and your customer record.\n *\n * When validating a key, we will return this back to you, so you can clearly identify your user from their api key.\n */\n ownerId?: string;\n /**\n * This is a place for dynamic meta data, anything that feels useful for you should go here\n *\n * Example:\n *\n * ```json\n * {\n * \"billingTier\":\"PRO\",\n * \"trialEnds\": \"2023-06-16T17:16:37.161Z\"\n * }\n * ```\n */\n meta?: unknown;\n /**\n * You can auto expire keys by providing a unix timestamp in milliseconds.\n *\n * Once keys expire they will automatically be deleted and are no longer valid.\n */\n expires?: number;\n\n /**\n * Unkey comes with per-key ratelimiting out of the box.\n *\n * @see https://unkey.dev/docs/features/ratelimiting\n */\n ratelimit?: {\n type: \"fast\" | \"consistent\";\n /**\n * The total amount of burstable requests.\n */\n limit: number;\n\n /**\n * How many tokens to refill during each refillInterval\n */\n refillRate: number;\n\n /**\n * Determines the speed at which tokens are refilled.\n * In milliseconds.\n */\n refillInterval: number;\n };\n\n /**\n * Unkey allows you to set/update usage limits on individual keys\n *\n * @see https://unkey.dev/docs/features/remaining\n */\n remaining?: number;\n }): Promise<Result<{ key: string; keyId: string }>> => {\n return await this.fetch<{ key: string; keyId: string }>({\n path: [\"v1\", \"keys\"],\n method: \"POST\",\n body: req,\n });\n },\n update: async (req: {\n /**\n * The id of the key to update.\n */\n keyId: string;\n /**\n * Update the name\n */\n name?: string | null;\n\n /**\n * Update the owner id\n */\n ownerId?: string | null;\n /**\n * This is a place for dynamic meta data, anything that feels useful for you should go here\n *\n * Example:\n *\n * ```json\n * {\n * \"billingTier\":\"PRO\",\n * \"trialEnds\": \"2023-06-16T17:16:37.161Z\"\n * }\n * ```\n */\n meta?: unknown | null;\n /**\n * Update the expiration time, Unix timstamp in milliseconds\n *\n *\n */\n expires?: number | null;\n\n /**\n * Update the ratelimit\n *\n * @see https://unkey.dev/docs/features/ratelimiting\n */\n ratelimit?: {\n type: \"fast\" | \"consistent\";\n /**\n * The total amount of burstable requests.\n */\n limit: number;\n\n /**\n * How many tokens to refill during each refillInterval\n */\n refillRate: number;\n\n /**\n * Determines the speed at which tokens are refilled.\n * In milliseconds.\n */\n refillInterval: number;\n } | null;\n\n /**\n * Update the remaining verifications.\n *\n * @see https://unkey.dev/docs/features/remaining\n */\n remaining?: number | null;\n }): Promise<Result<{ key: string; keyId: string }>> => {\n return await this.fetch<{ key: string; keyId: string }>({\n path: [\"v1\", \"keys\", req.keyId],\n method: \"PUT\",\n body: req,\n });\n },\n verify: async (req: {\n /**\n * The key to verify\n */\n key: string;\n\n /**\n * The api id to verify against\n *\n * This will be required soon.\n */\n apiId?: string;\n }): Promise<\n Result<{\n /**\n * Whether or not this key is valid and has passed the ratelimit. If false you should not grant access to whatever the user is requesting\n */\n valid: boolean;\n\n /**\n * If you have set an ownerId on this key it is returned here. You can use this to clearly authenticate a user in your system.\n */\n ownerId?: string;\n\n /**\n * This is the meta data you have set when creating the key.\n *\n * Example:\n *\n * ```json\n * {\n * \"billingTier\":\"PRO\",\n * \"trialEnds\": \"2023-06-16T17:16:37.161Z\"\n * }\n * ```\n */\n meta?: unknown;\n\n /**\n * Unix timestamp in milliseconds when this key expires\n * Only available when the key automatically expires\n */\n expires?: number;\n\n /**\n * How many verifications are remaining after the current request.\n */\n remaining?: number;\n\n /**\n * Ratelimit data if the key is ratelimited.\n */\n ratelimit?: {\n /**\n * The maximum number of requests for bursting.\n */\n limit: number;\n\n /**\n * How many requests are remaining until `reset`\n */\n remaining: number;\n\n /**\n * Unix timestamp in millisecond when the ratelimit is refilled.\n */\n reset: number;\n };\n\n /**\n * Machine readable code that explains why a key is invalid or could not be verified\n */\n code?: \"NOT_FOUND\" | \"FORBIDDEN\" | \"RATELIMITED\" | \"KEY_USAGE_EXCEEDED\";\n }>\n > => {\n return await this.fetch<{\n valid: boolean;\n ownerId?: string;\n meta?: unknown;\n }>({\n path: [\"v1\", \"keys\", \"verify\"],\n method: \"POST\",\n body: req,\n });\n },\n revoke: async (req: { keyId: string }): Promise<Result<void>> => {\n return await this.fetch<void>({\n path: [\"v1\", \"keys\", req.keyId],\n method: \"DELETE\",\n });\n },\n };\n }\n\n public get apis() {\n return {\n create: async (req: {\n /**\n * A name for you to identify your API.\n */\n name: string;\n }): Promise<\n Result<{\n /**\n * The global unique identifier of your api.\n * You'll need this for other api requests.\n */\n apiId: string;\n }>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.createApi\"],\n method: \"POST\",\n body: req,\n });\n },\n remove: async (req: {\n /**\n * The global unique identifier of your api.\n * You'll need this for other api requests.\n */\n apiId: string;\n }): Promise<\n Result<{\n /**\n * The global unique identifier of your api.\n * You'll need this for other api requests.\n */\n apiId: string;\n }>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.removeApi\"],\n method: \"POST\",\n body: req,\n });\n },\n get: async (req: {\n /**\n * The api id\n */\n apiId: string;\n }): Promise<Result<{ id: string; name: string; workspaceId: string }>> => {\n return await this.fetch({\n path: [\"v1\", \"apis\", req.apiId],\n method: \"GET\",\n });\n },\n\n listKeys: async (req: {\n /**\n * The api id\n */\n apiId: string;\n\n /**\n * Limit the number of returned keys, the maximum is 100.\n *\n * @default 100\n */\n limit?: number;\n\n /**\n * Specify an offset for pagination.\n * @example:\n * An offset of 4 will skip the first 4 keys and return keys starting at the 5th position.\n *\n * @default 0\n */\n offset?: number;\n\n /**\n * If provided, this will only return keys where the ownerId matches.\n */\n ownerId?: string;\n }): Promise<\n Result<{\n keys: {\n id: string;\n apiId: string;\n ownerId?: string;\n workspaceId: string;\n start: string;\n createdAt: number;\n name?: string;\n expires?: number;\n remaining?: number;\n meta?: unknown;\n ratelimit?: {\n type: \"fast\" | \"consistent\";\n limit: number;\n refillRate: number;\n refillInterval: number;\n };\n }[];\n total: number;\n }>\n > => {\n const query: Record<string, string> = {};\n if (typeof req.limit !== \"undefined\") {\n query.limit = req.limit.toString();\n }\n if (typeof req.offset !== \"undefined\") {\n query.offset = req.offset.toString();\n }\n if (typeof req.ownerId !== \"undefined\") {\n query.ownerId = req.ownerId;\n }\n\n return await this.fetch({\n path: [\"v1\", \"apis\", req.apiId, \"keys\"],\n method: \"GET\",\n query,\n });\n },\n };\n }\n /**\n * Must be authenticated via app token\n */\n public get _internal() {\n return {\n createRootKey: async (req: {\n /**\n * Provide a name to this key if you want for later reference\n */\n name?: string;\n\n /**\n * You can auto expire keys by providing a unix timestamp in milliseconds.\n *\n * Once keys expire they will automatically be deleted and are no longer valid.\n */\n expires?: number;\n\n // Used to create root keys from the frontend, please ignore\n forWorkspaceId: string;\n }): Promise<Result<{ key: string; keyId: string }>> => {\n return await this.fetch<{ key: string; keyId: string }>({\n path: [\"v1\", \"internal\", \"rootkeys\"],\n method: \"POST\",\n body: req,\n });\n },\n deleteRootKey: async (req: {\n /**\n * Used to create root keys from the frontend, please ignore\n */\n keyId: string;\n }): Promise<Result<void>> => {\n return await this.fetch<void>({\n path: [\"v1\", \"internal.removeRootKey\"],\n method: \"POST\",\n body: req,\n });\n },\n };\n }\n}\n","import { Unkey } from \"./client\";\n\n/**\n * Verify a key\n *\n * @example\n * ```ts\n * const { result, error } = await verifyKey(\"key_123\")\n * if (error){\n * // handle potential network or bad request error\n * // a link to our docs will be in the `error.docs` field\n * console.error(error.message)\n * return\n * }\n * if (!result.valid) {\n * // do not grant access\n * return\n * }\n *\n * // process request\n * console.log(result)\n * ```\n */\nexport function verifyKey(req: string | { key: string; apiId: string }) {\n // yes this is empty to make typescript happy but we don't need a token for verifying keys\n // it's not the cleanest but it works for now :)\n const unkey = new Unkey({ token: \"public\" });\n return unkey.keys.verify(typeof req === \"string\" ? { key: req } : req);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../package.json","../src/client.ts","../src/verify.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./verify\";\nexport * from \"./errors\";\n","{\n \"name\": \"@unkey/api\",\n \"version\": \"0.13.1\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"license\": \"MIT\",\n \"private\": false,\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"keywords\": [\n \"unkey\",\n \"client\",\n \"api\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/unkeyed/unkey/issues\"\n },\n \"homepage\": \"https://github.com/unkeyed/unkey#readme\",\n \"files\": [\n \"./dist/**\"\n ],\n \"author\": \"Andreas Thomas <andreas@chronark.com>\",\n \"scripts\": {\n \"test\": \"bun test\",\n \"test:coverage\": \"bun test --coverage\",\n \"generate\": \"openapi-typescript https://api.unkey.dev/openapi.json -o ./src/openapi.d.ts\",\n \"build\": \"pnpm generate && tsup\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.8.7\",\n \"openapi-typescript\": \"^6.7.2\",\n \"tsup\": \"^7.2.0\",\n \"tsx\": \"^3.14.0\",\n \"typescript\": \"^5.2.2\"\n }\n}\n","import { version } from \"../package.json\";\nimport { ErrorResponse } from \"./errors\";\nimport type { paths } from \"./openapi\";\nexport type UnkeyOptions = (\n | {\n token?: never;\n\n /**\n * The root key from unkey.dev.\n *\n * You can create/manage your root keys here:\n * https://unkey.dev/app/settings/root-keys\n */\n rootKey: string;\n }\n | {\n /**\n * The workspace key from unkey.dev\n *\n * @deprecated Use `rootKey`\n */\n token: string;\n rootKey?: never;\n }\n) & {\n /**\n * @default https://api.unkey.dev\n */\n baseUrl?: string;\n\n /**\n * Retry on network errors\n */\n retry?: {\n /**\n * How many attempts should be made\n * The maximum number of requests will be `attempts + 1`\n * `0` means no retries\n *\n * @default 5\n */\n attempts?: number;\n /**\n * Return how many milliseconds to wait until the next attempt is made\n *\n * @default `(retryCount) => Math.round(Math.exp(retryCount) * 10)),`\n */\n backoff?: (retryCount: number) => number;\n };\n /**\n * Customize the `fetch` cache behaviour\n */\n cache?: RequestCache;\n\n /**\n * The version of the SDK instantiating this client.\n *\n * This is used for internal metrics and is not covered by semver, and may change at any time.\n *\n * You can leave this blank unless you are building a wrapper around this SDK.\n */\n wrapperSdkVersion?: `v${string}`;\n};\n\ntype ApiRequest = {\n path: string[];\n} & (\n | {\n method: \"GET\";\n body?: never;\n query?: Record<string, string | number | boolean>;\n }\n | {\n method: \"POST\";\n body?: unknown;\n query?: never;\n }\n);\n\ntype Result<R> =\n | {\n result: R;\n error?: never;\n }\n | {\n result?: never;\n error: ErrorResponse[\"error\"];\n };\n\nexport class Unkey {\n public readonly baseUrl: string;\n private readonly rootKey: string;\n private readonly cache?: RequestCache;\n private readonly sdkVersions: `v${string}`[] = [];\n\n public readonly retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n constructor(opts: UnkeyOptions) {\n this.baseUrl = opts.baseUrl ?? \"https://api.unkey.dev\";\n this.rootKey = opts.rootKey ?? opts.token;\n this.sdkVersions.push(`v${version}`);\n if (opts.wrapperSdkVersion) {\n this.sdkVersions.push(opts.wrapperSdkVersion);\n }\n\n this.cache = opts.cache;\n /**\n * Even though typescript should prevent this, some people still pass undefined or empty strings\n */\n if (!this.rootKey) {\n throw new Error(\n \"Unkey root key must be set, maybe you passed in `undefined` or an empty string?\",\n );\n }\n\n this.retry = {\n attempts: opts.retry?.attempts ?? 5,\n backoff: opts.retry?.backoff ?? ((n) => Math.round(Math.exp(n) * 10)),\n };\n }\n\n private async fetch<TResult>(req: ApiRequest): Promise<Result<TResult>> {\n let res: Response | null = null;\n let err: Error | null = null;\n for (let i = 0; i <= this.retry.attempts; i++) {\n const url = new URL(`${this.baseUrl}/${req.path.join(\"/\")}`);\n if (req.query) {\n for (const [k, v] of Object.entries(req.query)) {\n url.searchParams.set(k, v.toString());\n }\n }\n res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.rootKey}`,\n \"Unkey-SDK\": this.sdkVersions.join(\",\"),\n },\n cache: this.cache,\n body: JSON.stringify(req.body),\n }).catch((e: Error) => {\n err = e;\n return null; // set `res` to `null`\n });\n if (res?.ok) {\n return { result: (await res.json()) as TResult };\n }\n const backoff = this.retry.backoff(i);\n console.debug(\n \"attempt %d of %d to reach %s failed, retrying in %d ms: %s\",\n i + 1,\n this.retry.attempts + 1,\n url,\n backoff,\n // @ts-ignore I don't understand why `err` is `never`\n err?.message,\n );\n await new Promise((r) => setTimeout(r, backoff));\n }\n\n if (res) {\n return { error: (await res.json()) as ErrorResponse[\"error\"] };\n }\n\n return {\n error: {\n // @ts-ignore\n code: \"FETCH_ERROR\",\n // @ts-ignore I don't understand why `err` is `never`\n message: err?.message ?? \"No response\",\n docs: \"https://developer.mozilla.org/en-US/docs/Web/API/fetch\",\n requestId: \"N/A\",\n },\n };\n }\n\n public get keys() {\n return {\n create: async (\n req: paths[\"/v1/keys.createKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.createKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.createKey\"],\n method: \"POST\",\n body: req,\n });\n },\n update: async (\n req: paths[\"/v1/keys.updateKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.updateKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.updateKey\"],\n method: \"POST\",\n body: req,\n });\n },\n verify: async (\n req: paths[\"/v1/keys.verifyKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.verifyKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.verifyKey\"],\n method: \"POST\",\n body: req,\n });\n },\n delete: async (\n req: paths[\"/v1/keys.deleteKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.deleteKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.deleteKey\"],\n method: \"POST\",\n body: req,\n });\n },\n updateRemaining: async (\n req: paths[\"/v1/keys.updateRemaining\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.updateRemaining\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.updateRemaining\"],\n method: \"POST\",\n body: req,\n });\n },\n get: async (\n req: paths[\"/v1/keys.getKey\"][\"get\"][\"parameters\"][\"query\"],\n ): Promise<\n Result<paths[\"/v1/keys.getKey\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]>\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.getKey\"],\n method: \"GET\",\n query: req,\n });\n },\n };\n }\n\n public get apis() {\n return {\n create: async (\n req: paths[\"/v1/apis.createApi\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/apis.createApi\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.createApi\"],\n method: \"POST\",\n body: req,\n });\n },\n delete: async (\n req: paths[\"/v1/apis.deleteApi\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/apis.deleteApi\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.deleteApi\"],\n method: \"POST\",\n body: req,\n });\n },\n get: async (\n req: paths[\"/v1/apis.getApi\"][\"get\"][\"parameters\"][\"query\"],\n ): Promise<\n Result<paths[\"/v1/apis.getApi\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.getApi\"],\n method: \"GET\",\n query: req,\n });\n },\n listKeys: async (\n req: paths[\"/v1/apis.listKeys\"][\"get\"][\"parameters\"][\"query\"],\n ): Promise<\n Result<paths[\"/v1/apis.listKeys\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.listKeys\"],\n method: \"GET\",\n query: req,\n });\n },\n };\n }\n}\n","import { Unkey } from \"./client\";\n\n/**\n * Verify a key\n *\n * @example\n * ```ts\n * const { result, error } = await verifyKey(\"key_123\")\n * if (error){\n * // handle potential network or bad request error\n * // a link to our docs will be in the `error.docs` field\n * console.error(error.message)\n * return\n * }\n * if (!result.valid) {\n * // do not grant access\n * return\n * }\n *\n * // process request\n * console.log(result)\n * ```\n */\nexport function verifyKey(req: string | { key: string; apiId: string }) {\n // yes this is empty to make typescript happy but we don't need a token for verifying keys\n // it's not the cleanest but it works for now :)\n const unkey = new Unkey({ rootKey: \"public\" });\n return unkey.keys.verify(typeof req === \"string\" ? { key: req } : req);\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -1,12 +1,20 @@
1
+ // package.json
2
+ var version = "0.13.1";
3
+
1
4
  // src/client.ts
2
5
  var Unkey = class {
3
6
  baseUrl;
4
7
  rootKey;
5
8
  cache;
9
+ sdkVersions = [];
6
10
  retry;
7
11
  constructor(opts) {
8
12
  this.baseUrl = opts.baseUrl ?? "https://api.unkey.dev";
9
13
  this.rootKey = opts.rootKey ?? opts.token;
14
+ this.sdkVersions.push(`v${version}`);
15
+ if (opts.wrapperSdkVersion) {
16
+ this.sdkVersions.push(opts.wrapperSdkVersion);
17
+ }
10
18
  this.cache = opts.cache;
11
19
  if (!this.rootKey) {
12
20
  throw new Error(
@@ -25,14 +33,15 @@ var Unkey = class {
25
33
  const url = new URL(`${this.baseUrl}/${req.path.join("/")}`);
26
34
  if (req.query) {
27
35
  for (const [k, v] of Object.entries(req.query)) {
28
- url.searchParams.set(k, v);
36
+ url.searchParams.set(k, v.toString());
29
37
  }
30
38
  }
31
39
  res = await fetch(url, {
32
40
  method: req.method,
33
41
  headers: {
34
42
  "Content-Type": "application/json",
35
- Authorization: `Bearer ${this.rootKey}`
43
+ Authorization: `Bearer ${this.rootKey}`,
44
+ "Unkey-SDK": this.sdkVersions.join(",")
36
45
  },
37
46
  cache: this.cache,
38
47
  body: JSON.stringify(req.body)
@@ -60,6 +69,7 @@ var Unkey = class {
60
69
  }
61
70
  return {
62
71
  error: {
72
+ // @ts-ignore
63
73
  code: "FETCH_ERROR",
64
74
  // @ts-ignore I don't understand why `err` is `never`
65
75
  message: err?.message ?? "No response",
@@ -72,92 +82,77 @@ var Unkey = class {
72
82
  return {
73
83
  create: async (req) => {
74
84
  return await this.fetch({
75
- path: ["v1", "keys"],
85
+ path: ["v1", "keys.createKey"],
76
86
  method: "POST",
77
87
  body: req
78
88
  });
79
89
  },
80
90
  update: async (req) => {
81
91
  return await this.fetch({
82
- path: ["v1", "keys", req.keyId],
83
- method: "PUT",
92
+ path: ["v1", "keys.updateKey"],
93
+ method: "POST",
84
94
  body: req
85
95
  });
86
96
  },
87
97
  verify: async (req) => {
88
98
  return await this.fetch({
89
- path: ["v1", "keys", "verify"],
99
+ path: ["v1", "keys.verifyKey"],
90
100
  method: "POST",
91
101
  body: req
92
102
  });
93
103
  },
94
- revoke: async (req) => {
95
- return await this.fetch({
96
- path: ["v1", "keys", req.keyId],
97
- method: "DELETE"
98
- });
99
- }
100
- };
101
- }
102
- get apis() {
103
- return {
104
- create: async (req) => {
104
+ delete: async (req) => {
105
105
  return await this.fetch({
106
- path: ["v1", "apis.createApi"],
106
+ path: ["v1", "keys.deleteKey"],
107
107
  method: "POST",
108
108
  body: req
109
109
  });
110
110
  },
111
- remove: async (req) => {
111
+ updateRemaining: async (req) => {
112
112
  return await this.fetch({
113
- path: ["v1", "apis.removeApi"],
113
+ path: ["v1", "keys.updateRemaining"],
114
114
  method: "POST",
115
115
  body: req
116
116
  });
117
117
  },
118
118
  get: async (req) => {
119
119
  return await this.fetch({
120
- path: ["v1", "apis", req.apiId],
121
- method: "GET"
122
- });
123
- },
124
- listKeys: async (req) => {
125
- const query = {};
126
- if (typeof req.limit !== "undefined") {
127
- query.limit = req.limit.toString();
128
- }
129
- if (typeof req.offset !== "undefined") {
130
- query.offset = req.offset.toString();
131
- }
132
- if (typeof req.ownerId !== "undefined") {
133
- query.ownerId = req.ownerId;
134
- }
135
- return await this.fetch({
136
- path: ["v1", "apis", req.apiId, "keys"],
120
+ path: ["v1", "keys.getKey"],
137
121
  method: "GET",
138
- query
122
+ query: req
139
123
  });
140
124
  }
141
125
  };
142
126
  }
143
- /**
144
- * Must be authenticated via app token
145
- */
146
- get _internal() {
127
+ get apis() {
147
128
  return {
148
- createRootKey: async (req) => {
129
+ create: async (req) => {
149
130
  return await this.fetch({
150
- path: ["v1", "internal", "rootkeys"],
131
+ path: ["v1", "apis.createApi"],
151
132
  method: "POST",
152
133
  body: req
153
134
  });
154
135
  },
155
- deleteRootKey: async (req) => {
136
+ delete: async (req) => {
156
137
  return await this.fetch({
157
- path: ["v1", "internal.removeRootKey"],
138
+ path: ["v1", "apis.deleteApi"],
158
139
  method: "POST",
159
140
  body: req
160
141
  });
142
+ },
143
+ get: async (req) => {
144
+ return await this.fetch({
145
+ path: ["v1", "apis.getApi"],
146
+ method: "GET",
147
+ query: req
148
+ });
149
+ },
150
+ listKeys: async (req) => {
151
+ return await this.fetch({
152
+ path: ["v1", "apis.listKeys"],
153
+ method: "GET",
154
+ query: req
155
+ });
161
156
  }
162
157
  };
163
158
  }
@@ -165,7 +160,7 @@ var Unkey = class {
165
160
 
166
161
  // src/verify.ts
167
162
  function verifyKey(req) {
168
- const unkey = new Unkey({ token: "public" });
163
+ const unkey = new Unkey({ rootKey: "public" });
169
164
  return unkey.keys.verify(typeof req === "string" ? { key: req } : req);
170
165
  }
171
166
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/verify.ts"],"sourcesContent":["import { UnkeyError } from \"./errors\";\n\nexport type UnkeyOptions = (\n | {\n token?: never;\n\n /**\n * The root key from unkey.dev.\n *\n * You can create/manage your root keys here:\n * https://unkey.dev/app/settings/root-keys\n */\n rootKey: string;\n }\n | {\n /**\n * The workspace key from unkey.dev\n *\n * @deprecated Use `rootKey`\n */\n token: string;\n rootKey?: never;\n }\n) & {\n /**\n * @default https://api.unkey.dev\n */\n baseUrl?: string;\n\n /**\n * Retry on network errors\n */\n retry?: {\n /**\n * How many attempts should be made\n * The maximum number of requests will be `attempts + 1`\n * `0` means no retries\n *\n * @default 5\n */\n attempts?: number;\n /**\n * Return how many milliseconds to wait until the next attempt is made\n *\n * @default `(retryCount) => Math.round(Math.exp(retryCount) * 10)),`\n */\n backoff?: (retryCount: number) => number;\n };\n /**\n * Customize the `fetch` cache behaviour\n */\n cache?: RequestCache;\n // some change\n};\n\ntype ApiRequest = {\n path: string[];\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n query?: Record<string, string>;\n};\n\ntype Result<R> =\n | {\n result: R;\n error?: never;\n }\n | {\n result?: never;\n error: UnkeyError;\n };\n\nexport class Unkey {\n public readonly baseUrl: string;\n private readonly rootKey: string;\n private readonly cache?: RequestCache;\n\n public readonly retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n constructor(opts: UnkeyOptions) {\n this.baseUrl = opts.baseUrl ?? \"https://api.unkey.dev\";\n this.rootKey = opts.rootKey ?? opts.token;\n\n this.cache = opts.cache;\n /**\n * Even though typescript should prevent this, some people still pass undefined or empty strings\n */\n if (!this.rootKey) {\n throw new Error(\n \"Unkey root key must be set, maybe you passed in `undefined` or an empty string?\",\n );\n }\n\n this.retry = {\n attempts: opts.retry?.attempts ?? 5,\n backoff: opts.retry?.backoff ?? ((n) => Math.round(Math.exp(n) * 10)),\n };\n }\n\n private async fetch<TResult>(req: ApiRequest): Promise<Result<TResult>> {\n let res: Response | null = null;\n let err: Error | null = null;\n for (let i = 0; i <= this.retry.attempts; i++) {\n const url = new URL(`${this.baseUrl}/${req.path.join(\"/\")}`);\n if (req.query) {\n for (const [k, v] of Object.entries(req.query)) {\n url.searchParams.set(k, v);\n }\n }\n res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.rootKey}`,\n },\n cache: this.cache,\n body: JSON.stringify(req.body),\n }).catch((e: Error) => {\n err = e;\n return null; // set `res` to `null`\n });\n if (res?.ok) {\n return { result: (await res.json()) as TResult };\n }\n const backoff = this.retry.backoff(i);\n console.debug(\n \"attempt %d of %d to reach %s failed, retrying in %d ms: %s\",\n i + 1,\n this.retry.attempts + 1,\n url,\n backoff,\n // @ts-ignore I don't understand why `err` is `never`\n err?.message,\n );\n await new Promise((r) => setTimeout(r, backoff));\n }\n\n if (res) {\n return { error: (await res.json()) as UnkeyError };\n }\n\n return {\n error: {\n code: \"FETCH_ERROR\",\n // @ts-ignore I don't understand why `err` is `never`\n message: err?.message ?? \"No response\",\n docs: \"https://developer.mozilla.org/en-US/docs/Web/API/fetch\",\n requestId: \"N/A\",\n },\n };\n }\n\n public get keys() {\n return {\n create: async (req: {\n /**\n * Provide a name to this key if you want for later reference\n */\n name?: string;\n /**\n * Choose an API where this key should be created.\n */\n apiId: string;\n /**\n * To make it easier for your users to understand which product an api key belongs to, you can add prefix them.\n *\n * For example Stripe famously prefixes their customer ids with cus_ or their api keys with sk_live_.\n *\n * The underscore is automtically added if you are defining a prefix, for example: \"prefix\": \"abc\" will result in a key like abc_xxxxxxxxx\n */\n prefix?: string;\n\n /**\n * The bytelength used to generate your key determines its entropy as well as its length. Higher is better, but keys become longer and more annoying to handle.\n *\n * The default is 16 bytes, or 2128 possible combinations\n */\n byteLength?: number;\n /**\n * Your user’s Id. This will provide a link between Unkey and your customer record.\n *\n * When validating a key, we will return this back to you, so you can clearly identify your user from their api key.\n */\n ownerId?: string;\n /**\n * This is a place for dynamic meta data, anything that feels useful for you should go here\n *\n * Example:\n *\n * ```json\n * {\n * \"billingTier\":\"PRO\",\n * \"trialEnds\": \"2023-06-16T17:16:37.161Z\"\n * }\n * ```\n */\n meta?: unknown;\n /**\n * You can auto expire keys by providing a unix timestamp in milliseconds.\n *\n * Once keys expire they will automatically be deleted and are no longer valid.\n */\n expires?: number;\n\n /**\n * Unkey comes with per-key ratelimiting out of the box.\n *\n * @see https://unkey.dev/docs/features/ratelimiting\n */\n ratelimit?: {\n type: \"fast\" | \"consistent\";\n /**\n * The total amount of burstable requests.\n */\n limit: number;\n\n /**\n * How many tokens to refill during each refillInterval\n */\n refillRate: number;\n\n /**\n * Determines the speed at which tokens are refilled.\n * In milliseconds.\n */\n refillInterval: number;\n };\n\n /**\n * Unkey allows you to set/update usage limits on individual keys\n *\n * @see https://unkey.dev/docs/features/remaining\n */\n remaining?: number;\n }): Promise<Result<{ key: string; keyId: string }>> => {\n return await this.fetch<{ key: string; keyId: string }>({\n path: [\"v1\", \"keys\"],\n method: \"POST\",\n body: req,\n });\n },\n update: async (req: {\n /**\n * The id of the key to update.\n */\n keyId: string;\n /**\n * Update the name\n */\n name?: string | null;\n\n /**\n * Update the owner id\n */\n ownerId?: string | null;\n /**\n * This is a place for dynamic meta data, anything that feels useful for you should go here\n *\n * Example:\n *\n * ```json\n * {\n * \"billingTier\":\"PRO\",\n * \"trialEnds\": \"2023-06-16T17:16:37.161Z\"\n * }\n * ```\n */\n meta?: unknown | null;\n /**\n * Update the expiration time, Unix timstamp in milliseconds\n *\n *\n */\n expires?: number | null;\n\n /**\n * Update the ratelimit\n *\n * @see https://unkey.dev/docs/features/ratelimiting\n */\n ratelimit?: {\n type: \"fast\" | \"consistent\";\n /**\n * The total amount of burstable requests.\n */\n limit: number;\n\n /**\n * How many tokens to refill during each refillInterval\n */\n refillRate: number;\n\n /**\n * Determines the speed at which tokens are refilled.\n * In milliseconds.\n */\n refillInterval: number;\n } | null;\n\n /**\n * Update the remaining verifications.\n *\n * @see https://unkey.dev/docs/features/remaining\n */\n remaining?: number | null;\n }): Promise<Result<{ key: string; keyId: string }>> => {\n return await this.fetch<{ key: string; keyId: string }>({\n path: [\"v1\", \"keys\", req.keyId],\n method: \"PUT\",\n body: req,\n });\n },\n verify: async (req: {\n /**\n * The key to verify\n */\n key: string;\n\n /**\n * The api id to verify against\n *\n * This will be required soon.\n */\n apiId?: string;\n }): Promise<\n Result<{\n /**\n * Whether or not this key is valid and has passed the ratelimit. If false you should not grant access to whatever the user is requesting\n */\n valid: boolean;\n\n /**\n * If you have set an ownerId on this key it is returned here. You can use this to clearly authenticate a user in your system.\n */\n ownerId?: string;\n\n /**\n * This is the meta data you have set when creating the key.\n *\n * Example:\n *\n * ```json\n * {\n * \"billingTier\":\"PRO\",\n * \"trialEnds\": \"2023-06-16T17:16:37.161Z\"\n * }\n * ```\n */\n meta?: unknown;\n\n /**\n * Unix timestamp in milliseconds when this key expires\n * Only available when the key automatically expires\n */\n expires?: number;\n\n /**\n * How many verifications are remaining after the current request.\n */\n remaining?: number;\n\n /**\n * Ratelimit data if the key is ratelimited.\n */\n ratelimit?: {\n /**\n * The maximum number of requests for bursting.\n */\n limit: number;\n\n /**\n * How many requests are remaining until `reset`\n */\n remaining: number;\n\n /**\n * Unix timestamp in millisecond when the ratelimit is refilled.\n */\n reset: number;\n };\n\n /**\n * Machine readable code that explains why a key is invalid or could not be verified\n */\n code?: \"NOT_FOUND\" | \"FORBIDDEN\" | \"RATELIMITED\" | \"KEY_USAGE_EXCEEDED\";\n }>\n > => {\n return await this.fetch<{\n valid: boolean;\n ownerId?: string;\n meta?: unknown;\n }>({\n path: [\"v1\", \"keys\", \"verify\"],\n method: \"POST\",\n body: req,\n });\n },\n revoke: async (req: { keyId: string }): Promise<Result<void>> => {\n return await this.fetch<void>({\n path: [\"v1\", \"keys\", req.keyId],\n method: \"DELETE\",\n });\n },\n };\n }\n\n public get apis() {\n return {\n create: async (req: {\n /**\n * A name for you to identify your API.\n */\n name: string;\n }): Promise<\n Result<{\n /**\n * The global unique identifier of your api.\n * You'll need this for other api requests.\n */\n apiId: string;\n }>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.createApi\"],\n method: \"POST\",\n body: req,\n });\n },\n remove: async (req: {\n /**\n * The global unique identifier of your api.\n * You'll need this for other api requests.\n */\n apiId: string;\n }): Promise<\n Result<{\n /**\n * The global unique identifier of your api.\n * You'll need this for other api requests.\n */\n apiId: string;\n }>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.removeApi\"],\n method: \"POST\",\n body: req,\n });\n },\n get: async (req: {\n /**\n * The api id\n */\n apiId: string;\n }): Promise<Result<{ id: string; name: string; workspaceId: string }>> => {\n return await this.fetch({\n path: [\"v1\", \"apis\", req.apiId],\n method: \"GET\",\n });\n },\n\n listKeys: async (req: {\n /**\n * The api id\n */\n apiId: string;\n\n /**\n * Limit the number of returned keys, the maximum is 100.\n *\n * @default 100\n */\n limit?: number;\n\n /**\n * Specify an offset for pagination.\n * @example:\n * An offset of 4 will skip the first 4 keys and return keys starting at the 5th position.\n *\n * @default 0\n */\n offset?: number;\n\n /**\n * If provided, this will only return keys where the ownerId matches.\n */\n ownerId?: string;\n }): Promise<\n Result<{\n keys: {\n id: string;\n apiId: string;\n ownerId?: string;\n workspaceId: string;\n start: string;\n createdAt: number;\n name?: string;\n expires?: number;\n remaining?: number;\n meta?: unknown;\n ratelimit?: {\n type: \"fast\" | \"consistent\";\n limit: number;\n refillRate: number;\n refillInterval: number;\n };\n }[];\n total: number;\n }>\n > => {\n const query: Record<string, string> = {};\n if (typeof req.limit !== \"undefined\") {\n query.limit = req.limit.toString();\n }\n if (typeof req.offset !== \"undefined\") {\n query.offset = req.offset.toString();\n }\n if (typeof req.ownerId !== \"undefined\") {\n query.ownerId = req.ownerId;\n }\n\n return await this.fetch({\n path: [\"v1\", \"apis\", req.apiId, \"keys\"],\n method: \"GET\",\n query,\n });\n },\n };\n }\n /**\n * Must be authenticated via app token\n */\n public get _internal() {\n return {\n createRootKey: async (req: {\n /**\n * Provide a name to this key if you want for later reference\n */\n name?: string;\n\n /**\n * You can auto expire keys by providing a unix timestamp in milliseconds.\n *\n * Once keys expire they will automatically be deleted and are no longer valid.\n */\n expires?: number;\n\n // Used to create root keys from the frontend, please ignore\n forWorkspaceId: string;\n }): Promise<Result<{ key: string; keyId: string }>> => {\n return await this.fetch<{ key: string; keyId: string }>({\n path: [\"v1\", \"internal\", \"rootkeys\"],\n method: \"POST\",\n body: req,\n });\n },\n deleteRootKey: async (req: {\n /**\n * Used to create root keys from the frontend, please ignore\n */\n keyId: string;\n }): Promise<Result<void>> => {\n return await this.fetch<void>({\n path: [\"v1\", \"internal.removeRootKey\"],\n method: \"POST\",\n body: req,\n });\n },\n };\n }\n}\n","import { Unkey } from \"./client\";\n\n/**\n * Verify a key\n *\n * @example\n * ```ts\n * const { result, error } = await verifyKey(\"key_123\")\n * if (error){\n * // handle potential network or bad request error\n * // a link to our docs will be in the `error.docs` field\n * console.error(error.message)\n * return\n * }\n * if (!result.valid) {\n * // do not grant access\n * return\n * }\n *\n * // process request\n * console.log(result)\n * ```\n */\nexport function verifyKey(req: string | { key: string; apiId: string }) {\n // yes this is empty to make typescript happy but we don't need a token for verifying keys\n // it's not the cleanest but it works for now :)\n const unkey = new Unkey({ token: \"public\" });\n return unkey.keys.verify(typeof req === \"string\" ? { key: req } : req);\n}\n"],"mappings":";AAwEO,IAAM,QAAN,MAAY;AAAA,EACD;AAAA,EACC;AAAA,EACA;AAAA,EAED;AAAA,EAKhB,YAAY,MAAoB;AAC9B,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,UAAU,KAAK,WAAW,KAAK;AAEpC,SAAK,QAAQ,KAAK;AAIlB,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,SAAK,QAAQ;AAAA,MACX,UAAU,KAAK,OAAO,YAAY;AAAA,MAClC,SAAS,KAAK,OAAO,YAAY,CAAC,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,EAAE;AAAA,IACrE;AAAA,EACF;AAAA,EAEA,MAAc,MAAe,KAA2C;AACtE,QAAI,MAAuB;AAC3B,QAAI,MAAoB;AACxB,aAAS,IAAI,GAAG,KAAK,KAAK,MAAM,UAAU,KAAK;AAC7C,YAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC,EAAE;AAC3D,UAAI,IAAI,OAAO;AACb,mBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,KAAK,GAAG;AAC9C,cAAI,aAAa,IAAI,GAAG,CAAC;AAAA,QAC3B;AAAA,MACF;AACA,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,QAAQ,IAAI;AAAA,QACZ,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,OAAO;AAAA,QACvC;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,MAAM,KAAK,UAAU,IAAI,IAAI;AAAA,MAC/B,CAAC,EAAE,MAAM,CAAC,MAAa;AACrB,cAAM;AACN,eAAO;AAAA,MACT,CAAC;AACD,UAAI,KAAK,IAAI;AACX,eAAO,EAAE,QAAS,MAAM,IAAI,KAAK,EAAc;AAAA,MACjD;AACA,YAAM,UAAU,KAAK,MAAM,QAAQ,CAAC;AACpC,cAAQ;AAAA,QACN;AAAA,QACA,IAAI;AAAA,QACJ,KAAK,MAAM,WAAW;AAAA,QACtB;AAAA,QACA;AAAA;AAAA,QAEA,KAAK;AAAA,MACP;AACA,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAAA,IACjD;AAEA,QAAI,KAAK;AACP,aAAO,EAAE,OAAQ,MAAM,IAAI,KAAK,EAAiB;AAAA,IACnD;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,QACL,MAAM;AAAA;AAAA,QAEN,SAAS,KAAK,WAAW;AAAA,QACzB,MAAM;AAAA,QACN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAW,OAAO;AAChB,WAAO;AAAA,MACL,QAAQ,OAAO,QAgFwC;AACrD,eAAO,MAAM,KAAK,MAAsC;AAAA,UACtD,MAAM,CAAC,MAAM,MAAM;AAAA,UACnB,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,OAAO,QAgEwC;AACrD,eAAO,MAAM,KAAK,MAAsC;AAAA,UACtD,MAAM,CAAC,MAAM,QAAQ,IAAI,KAAK;AAAA,UAC9B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,OAAO,QA0EV;AACH,eAAO,MAAM,KAAK,MAIf;AAAA,UACD,MAAM,CAAC,MAAM,QAAQ,QAAQ;AAAA,UAC7B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,OAAO,QAAkD;AAC/D,eAAO,MAAM,KAAK,MAAY;AAAA,UAC5B,MAAM,CAAC,MAAM,QAAQ,IAAI,KAAK;AAAA,UAC9B,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAW,OAAO;AAChB,WAAO;AAAA,MACL,QAAQ,OAAO,QAaV;AACH,eAAO,MAAM,KAAK,MAAM;AAAA,UACtB,MAAM,CAAC,MAAM,gBAAgB;AAAA,UAC7B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,OAAO,QAcV;AACH,eAAO,MAAM,KAAK,MAAM;AAAA,UACtB,MAAM,CAAC,MAAM,gBAAgB;AAAA,UAC7B,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,KAAK,OAAO,QAK8D;AACxE,eAAO,MAAM,KAAK,MAAM;AAAA,UACtB,MAAM,CAAC,MAAM,QAAQ,IAAI,KAAK;AAAA,UAC9B,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,MAEA,UAAU,OAAO,QAgDZ;AACH,cAAM,QAAgC,CAAC;AACvC,YAAI,OAAO,IAAI,UAAU,aAAa;AACpC,gBAAM,QAAQ,IAAI,MAAM,SAAS;AAAA,QACnC;AACA,YAAI,OAAO,IAAI,WAAW,aAAa;AACrC,gBAAM,SAAS,IAAI,OAAO,SAAS;AAAA,QACrC;AACA,YAAI,OAAO,IAAI,YAAY,aAAa;AACtC,gBAAM,UAAU,IAAI;AAAA,QACtB;AAEA,eAAO,MAAM,KAAK,MAAM;AAAA,UACtB,MAAM,CAAC,MAAM,QAAQ,IAAI,OAAO,MAAM;AAAA,UACtC,QAAQ;AAAA,UACR;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAIA,IAAW,YAAY;AACrB,WAAO;AAAA,MACL,eAAe,OAAO,QAeiC;AACrD,eAAO,MAAM,KAAK,MAAsC;AAAA,UACtD,MAAM,CAAC,MAAM,YAAY,UAAU;AAAA,UACnC,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,MACA,eAAe,OAAO,QAKO;AAC3B,eAAO,MAAM,KAAK,MAAY;AAAA,UAC5B,MAAM,CAAC,MAAM,wBAAwB;AAAA,UACrC,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACtiBO,SAAS,UAAU,KAA8C;AAGtE,QAAM,QAAQ,IAAI,MAAM,EAAE,OAAO,SAAS,CAAC;AAC3C,SAAO,MAAM,KAAK,OAAO,OAAO,QAAQ,WAAW,EAAE,KAAK,IAAI,IAAI,GAAG;AACvE;","names":[]}
1
+ {"version":3,"sources":["../package.json","../src/client.ts","../src/verify.ts"],"sourcesContent":["{\n \"name\": \"@unkey/api\",\n \"version\": \"0.13.1\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"license\": \"MIT\",\n \"private\": false,\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"keywords\": [\n \"unkey\",\n \"client\",\n \"api\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/unkeyed/unkey/issues\"\n },\n \"homepage\": \"https://github.com/unkeyed/unkey#readme\",\n \"files\": [\n \"./dist/**\"\n ],\n \"author\": \"Andreas Thomas <andreas@chronark.com>\",\n \"scripts\": {\n \"test\": \"bun test\",\n \"test:coverage\": \"bun test --coverage\",\n \"generate\": \"openapi-typescript https://api.unkey.dev/openapi.json -o ./src/openapi.d.ts\",\n \"build\": \"pnpm generate && tsup\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.8.7\",\n \"openapi-typescript\": \"^6.7.2\",\n \"tsup\": \"^7.2.0\",\n \"tsx\": \"^3.14.0\",\n \"typescript\": \"^5.2.2\"\n }\n}\n","import { version } from \"../package.json\";\nimport { ErrorResponse } from \"./errors\";\nimport type { paths } from \"./openapi\";\nexport type UnkeyOptions = (\n | {\n token?: never;\n\n /**\n * The root key from unkey.dev.\n *\n * You can create/manage your root keys here:\n * https://unkey.dev/app/settings/root-keys\n */\n rootKey: string;\n }\n | {\n /**\n * The workspace key from unkey.dev\n *\n * @deprecated Use `rootKey`\n */\n token: string;\n rootKey?: never;\n }\n) & {\n /**\n * @default https://api.unkey.dev\n */\n baseUrl?: string;\n\n /**\n * Retry on network errors\n */\n retry?: {\n /**\n * How many attempts should be made\n * The maximum number of requests will be `attempts + 1`\n * `0` means no retries\n *\n * @default 5\n */\n attempts?: number;\n /**\n * Return how many milliseconds to wait until the next attempt is made\n *\n * @default `(retryCount) => Math.round(Math.exp(retryCount) * 10)),`\n */\n backoff?: (retryCount: number) => number;\n };\n /**\n * Customize the `fetch` cache behaviour\n */\n cache?: RequestCache;\n\n /**\n * The version of the SDK instantiating this client.\n *\n * This is used for internal metrics and is not covered by semver, and may change at any time.\n *\n * You can leave this blank unless you are building a wrapper around this SDK.\n */\n wrapperSdkVersion?: `v${string}`;\n};\n\ntype ApiRequest = {\n path: string[];\n} & (\n | {\n method: \"GET\";\n body?: never;\n query?: Record<string, string | number | boolean>;\n }\n | {\n method: \"POST\";\n body?: unknown;\n query?: never;\n }\n);\n\ntype Result<R> =\n | {\n result: R;\n error?: never;\n }\n | {\n result?: never;\n error: ErrorResponse[\"error\"];\n };\n\nexport class Unkey {\n public readonly baseUrl: string;\n private readonly rootKey: string;\n private readonly cache?: RequestCache;\n private readonly sdkVersions: `v${string}`[] = [];\n\n public readonly retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n constructor(opts: UnkeyOptions) {\n this.baseUrl = opts.baseUrl ?? \"https://api.unkey.dev\";\n this.rootKey = opts.rootKey ?? opts.token;\n this.sdkVersions.push(`v${version}`);\n if (opts.wrapperSdkVersion) {\n this.sdkVersions.push(opts.wrapperSdkVersion);\n }\n\n this.cache = opts.cache;\n /**\n * Even though typescript should prevent this, some people still pass undefined or empty strings\n */\n if (!this.rootKey) {\n throw new Error(\n \"Unkey root key must be set, maybe you passed in `undefined` or an empty string?\",\n );\n }\n\n this.retry = {\n attempts: opts.retry?.attempts ?? 5,\n backoff: opts.retry?.backoff ?? ((n) => Math.round(Math.exp(n) * 10)),\n };\n }\n\n private async fetch<TResult>(req: ApiRequest): Promise<Result<TResult>> {\n let res: Response | null = null;\n let err: Error | null = null;\n for (let i = 0; i <= this.retry.attempts; i++) {\n const url = new URL(`${this.baseUrl}/${req.path.join(\"/\")}`);\n if (req.query) {\n for (const [k, v] of Object.entries(req.query)) {\n url.searchParams.set(k, v.toString());\n }\n }\n res = await fetch(url, {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.rootKey}`,\n \"Unkey-SDK\": this.sdkVersions.join(\",\"),\n },\n cache: this.cache,\n body: JSON.stringify(req.body),\n }).catch((e: Error) => {\n err = e;\n return null; // set `res` to `null`\n });\n if (res?.ok) {\n return { result: (await res.json()) as TResult };\n }\n const backoff = this.retry.backoff(i);\n console.debug(\n \"attempt %d of %d to reach %s failed, retrying in %d ms: %s\",\n i + 1,\n this.retry.attempts + 1,\n url,\n backoff,\n // @ts-ignore I don't understand why `err` is `never`\n err?.message,\n );\n await new Promise((r) => setTimeout(r, backoff));\n }\n\n if (res) {\n return { error: (await res.json()) as ErrorResponse[\"error\"] };\n }\n\n return {\n error: {\n // @ts-ignore\n code: \"FETCH_ERROR\",\n // @ts-ignore I don't understand why `err` is `never`\n message: err?.message ?? \"No response\",\n docs: \"https://developer.mozilla.org/en-US/docs/Web/API/fetch\",\n requestId: \"N/A\",\n },\n };\n }\n\n public get keys() {\n return {\n create: async (\n req: paths[\"/v1/keys.createKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.createKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.createKey\"],\n method: \"POST\",\n body: req,\n });\n },\n update: async (\n req: paths[\"/v1/keys.updateKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.updateKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.updateKey\"],\n method: \"POST\",\n body: req,\n });\n },\n verify: async (\n req: paths[\"/v1/keys.verifyKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.verifyKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.verifyKey\"],\n method: \"POST\",\n body: req,\n });\n },\n delete: async (\n req: paths[\"/v1/keys.deleteKey\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.deleteKey\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.deleteKey\"],\n method: \"POST\",\n body: req,\n });\n },\n updateRemaining: async (\n req: paths[\"/v1/keys.updateRemaining\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/keys.updateRemaining\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.updateRemaining\"],\n method: \"POST\",\n body: req,\n });\n },\n get: async (\n req: paths[\"/v1/keys.getKey\"][\"get\"][\"parameters\"][\"query\"],\n ): Promise<\n Result<paths[\"/v1/keys.getKey\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]>\n > => {\n return await this.fetch({\n path: [\"v1\", \"keys.getKey\"],\n method: \"GET\",\n query: req,\n });\n },\n };\n }\n\n public get apis() {\n return {\n create: async (\n req: paths[\"/v1/apis.createApi\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/apis.createApi\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.createApi\"],\n method: \"POST\",\n body: req,\n });\n },\n delete: async (\n req: paths[\"/v1/apis.deleteApi\"][\"post\"][\"requestBody\"][\"content\"][\"application/json\"],\n ): Promise<\n Result<\n paths[\"/v1/apis.deleteApi\"][\"post\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]\n >\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.deleteApi\"],\n method: \"POST\",\n body: req,\n });\n },\n get: async (\n req: paths[\"/v1/apis.getApi\"][\"get\"][\"parameters\"][\"query\"],\n ): Promise<\n Result<paths[\"/v1/apis.getApi\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.getApi\"],\n method: \"GET\",\n query: req,\n });\n },\n listKeys: async (\n req: paths[\"/v1/apis.listKeys\"][\"get\"][\"parameters\"][\"query\"],\n ): Promise<\n Result<paths[\"/v1/apis.listKeys\"][\"get\"][\"responses\"][\"200\"][\"content\"][\"application/json\"]>\n > => {\n return await this.fetch({\n path: [\"v1\", \"apis.listKeys\"],\n method: \"GET\",\n query: req,\n });\n },\n };\n }\n}\n","import { Unkey } from \"./client\";\n\n/**\n * Verify a key\n *\n * @example\n * ```ts\n * const { result, error } = await verifyKey(\"key_123\")\n * if (error){\n * // handle potential network or bad request error\n * // a link to our docs will be in the `error.docs` field\n * console.error(error.message)\n * return\n * }\n * if (!result.valid) {\n * // do not grant access\n * return\n * }\n *\n * // process request\n * console.log(result)\n * ```\n */\nexport function verifyKey(req: string | { key: string; apiId: string }) {\n // yes this is empty to make typescript happy but we don't need a token for verifying keys\n // it's not the cleanest but it works for now :)\n const unkey = new Unkey({ rootKey: \"public\" });\n return unkey.keys.verify(typeof req === \"string\" ? { key: req } : req);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unkey/api",
3
- "version": "0.12.1",
3
+ "version": "0.13.1",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "license": "MIT",
@@ -23,11 +23,15 @@
23
23
  "author": "Andreas Thomas <andreas@chronark.com>",
24
24
  "devDependencies": {
25
25
  "@types/node": "^20.8.7",
26
+ "openapi-typescript": "^6.7.2",
26
27
  "tsup": "^7.2.0",
27
28
  "tsx": "^3.14.0",
28
29
  "typescript": "^5.2.2"
29
30
  },
30
31
  "scripts": {
31
- "build": "tsup"
32
+ "test": "bun test",
33
+ "test:coverage": "bun test --coverage",
34
+ "generate": "openapi-typescript https://api.unkey.dev/openapi.json -o ./src/openapi.d.ts",
35
+ "build": "pnpm generate && tsup"
32
36
  }
33
37
  }