@donotlb/keypal 0.1.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/LICENSE +22 -0
- package/README.md +1037 -0
- package/dist/drizzle/schema.d.mts +87 -0
- package/dist/drizzle/schema.d.ts +87 -0
- package/dist/drizzle/schema.mjs +7 -0
- package/dist/index.d.mts +606 -0
- package/dist/index.d.ts +606 -0
- package/dist/index.mjs +7 -0
- package/dist/shared/keypal.C-UeOmUF.mjs +7 -0
- package/dist/shared/keypal.kItV-5pB.d.mts +194 -0
- package/dist/shared/keypal.kItV-5pB.d.ts +194 -0
- package/dist/shared/keypal.lTVSZWgp.mjs +7 -0
- package/dist/storage/drizzle.d.mts +192 -0
- package/dist/storage/drizzle.d.ts +192 -0
- package/dist/storage/drizzle.mjs +7 -0
- package/dist/storage/memory.d.mts +27 -0
- package/dist/storage/memory.d.ts +27 -0
- package/dist/storage/memory.mjs +7 -0
- package/dist/storage/redis.d.mts +42 -0
- package/dist/storage/redis.d.ts +42 -0
- package/dist/storage/redis.mjs +7 -0
- package/package.json +122 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { S as Storage, a as ApiKeyRecord, b as ApiKeyMetadata, d as AuditLog, c as AuditLogQuery, e as AuditLogStats } from '../shared/keypal.kItV-5pB.js';
|
|
2
|
+
import 'typebox';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Column mapping for API keys table
|
|
6
|
+
*/
|
|
7
|
+
type ApiKeyColumnMapping = {
|
|
8
|
+
/** Column name for the key ID (default: 'id') */
|
|
9
|
+
id?: string;
|
|
10
|
+
/** Column name for the hashed key (default: 'keyHash') */
|
|
11
|
+
keyHash?: string;
|
|
12
|
+
/** Column name for the metadata (default: 'metadata') */
|
|
13
|
+
metadata?: string;
|
|
14
|
+
/** Custom columns mapping for metadata fields */
|
|
15
|
+
metadataColumns?: {
|
|
16
|
+
ownerId?: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
scopes?: string;
|
|
20
|
+
resources?: string;
|
|
21
|
+
rateLimit?: string;
|
|
22
|
+
expiresAt?: string;
|
|
23
|
+
revokedAt?: string;
|
|
24
|
+
lastUsedAt?: string;
|
|
25
|
+
createdAt?: string;
|
|
26
|
+
tags?: string;
|
|
27
|
+
allowedIps?: string;
|
|
28
|
+
allowedOrigins?: string;
|
|
29
|
+
[key: string]: string | undefined;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Column mapping for audit logs table
|
|
34
|
+
*/
|
|
35
|
+
type AuditLogColumnMapping = {
|
|
36
|
+
id?: string;
|
|
37
|
+
keyId?: string;
|
|
38
|
+
ownerId?: string;
|
|
39
|
+
action?: string;
|
|
40
|
+
timestamp?: string;
|
|
41
|
+
data?: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Schema configuration for both tables
|
|
45
|
+
*/
|
|
46
|
+
type SchemaConfig = {
|
|
47
|
+
/** Table name for API keys (default: 'apikey' or 'apikeys') */
|
|
48
|
+
apiKeyTable?: string;
|
|
49
|
+
/** Column mappings for API keys table */
|
|
50
|
+
apiKeyColumns?: ApiKeyColumnMapping;
|
|
51
|
+
/** Table name for audit logs (default: 'auditlog' or 'auditlogs') */
|
|
52
|
+
auditLogTable?: string;
|
|
53
|
+
/** Column mappings for audit logs table */
|
|
54
|
+
auditLogColumns?: AuditLogColumnMapping;
|
|
55
|
+
/** Whether to use flattened schema (metadata as separate columns) */
|
|
56
|
+
flattenMetadata?: boolean;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generic database interface for Drizzle
|
|
61
|
+
* Supports any Drizzle database type (PostgreSQL, MySQL, SQLite)
|
|
62
|
+
*/
|
|
63
|
+
interface DrizzleDB {
|
|
64
|
+
[key: string]: any;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generic table interface for Drizzle
|
|
68
|
+
*/
|
|
69
|
+
interface DrizzleTable {
|
|
70
|
+
[key: string]: any;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Configuration for Drizzle adapter
|
|
74
|
+
*/
|
|
75
|
+
interface DrizzleAdapterConfig {
|
|
76
|
+
/**
|
|
77
|
+
* The Drizzle database instance
|
|
78
|
+
* Supports PostgreSQL, MySQL, and SQLite
|
|
79
|
+
*/
|
|
80
|
+
db: DrizzleDB;
|
|
81
|
+
/**
|
|
82
|
+
* The table for API keys
|
|
83
|
+
*/
|
|
84
|
+
table: DrizzleTable;
|
|
85
|
+
/**
|
|
86
|
+
* The database provider
|
|
87
|
+
* Used for provider-specific optimizations
|
|
88
|
+
*/
|
|
89
|
+
provider?: "pg" | "mysql" | "sqlite";
|
|
90
|
+
/**
|
|
91
|
+
* Schema configuration for custom column names and flattened metadata
|
|
92
|
+
*/
|
|
93
|
+
schema?: SchemaConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Optional table for audit logs
|
|
96
|
+
*/
|
|
97
|
+
auditLogTable?: DrizzleTable;
|
|
98
|
+
/**
|
|
99
|
+
* Enable debug logging
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
debugLogs?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a Drizzle storage adapter for API keys
|
|
106
|
+
*
|
|
107
|
+
* **Supports:**
|
|
108
|
+
* - PostgreSQL, MySQL, and SQLite
|
|
109
|
+
* - Custom column names
|
|
110
|
+
* - Custom table names
|
|
111
|
+
* - Flattened metadata schema
|
|
112
|
+
* - JSON/JSONB columns
|
|
113
|
+
* - Audit logging
|
|
114
|
+
*
|
|
115
|
+
* **Required Table Columns (default schema):**
|
|
116
|
+
* - `id`: TEXT PRIMARY KEY
|
|
117
|
+
* - `keyHash`: TEXT
|
|
118
|
+
* - `metadata`: JSONB (or TEXT for MySQL/SQLite)
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* // Default schema (PostgreSQL)
|
|
123
|
+
* import { createDrizzleStore } from 'keypal/drizzle';
|
|
124
|
+
* import { apikey } from 'keypal/drizzle/schema';
|
|
125
|
+
* const store = createDrizzleStore({ db, table: apikey, provider: 'pg' });
|
|
126
|
+
*
|
|
127
|
+
* // MySQL with custom column names
|
|
128
|
+
* const store = createDrizzleStore({
|
|
129
|
+
* db,
|
|
130
|
+
* table: customTable,
|
|
131
|
+
* provider: 'mysql',
|
|
132
|
+
* schema: {
|
|
133
|
+
* apiKeyColumns: {
|
|
134
|
+
* id: 'key_id',
|
|
135
|
+
* keyHash: 'key_hash',
|
|
136
|
+
* metadata: 'key_metadata'
|
|
137
|
+
* }
|
|
138
|
+
* }
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* // SQLite with flattened metadata
|
|
142
|
+
* const store = createDrizzleStore({
|
|
143
|
+
* db,
|
|
144
|
+
* table: flatTable,
|
|
145
|
+
* provider: 'sqlite',
|
|
146
|
+
* schema: {
|
|
147
|
+
* flattenMetadata: true,
|
|
148
|
+
* apiKeyColumns: {
|
|
149
|
+
* metadataColumns: {
|
|
150
|
+
* ownerId: 'owner_id',
|
|
151
|
+
* name: 'key_name',
|
|
152
|
+
* scopes: 'key_scopes'
|
|
153
|
+
* }
|
|
154
|
+
* }
|
|
155
|
+
* }
|
|
156
|
+
* });
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare function createDrizzleStore(options: DrizzleAdapterConfig): Storage;
|
|
160
|
+
/**
|
|
161
|
+
* Storage adapter class for Drizzle ORM
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const store = new DrizzleStore({ db, table: apikey, provider: 'pg' });
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare class DrizzleStore implements Storage {
|
|
169
|
+
private readonly storage;
|
|
170
|
+
constructor(options: DrizzleAdapterConfig);
|
|
171
|
+
save: (record: ApiKeyRecord) => Promise<void>;
|
|
172
|
+
findByHash: (keyHash: string) => Promise<ApiKeyRecord | null>;
|
|
173
|
+
findById: (id: string) => Promise<ApiKeyRecord | null>;
|
|
174
|
+
findByOwner: (ownerId: string) => Promise<ApiKeyRecord[]>;
|
|
175
|
+
findByTags: (tags: string[], ownerId?: string) => Promise<ApiKeyRecord[]>;
|
|
176
|
+
findByTag: (tag: string, ownerId?: string) => Promise<ApiKeyRecord[]>;
|
|
177
|
+
updateMetadata: (id: string, metadata: Partial<ApiKeyMetadata>) => Promise<void>;
|
|
178
|
+
delete: (id: string) => Promise<void>;
|
|
179
|
+
deleteByOwner: (ownerId: string) => Promise<void>;
|
|
180
|
+
saveLog: (log: AuditLog) => Promise<void>;
|
|
181
|
+
findLogs: (query: AuditLogQuery) => Promise<AuditLog[]>;
|
|
182
|
+
countLogs: (query: AuditLogQuery) => Promise<number>;
|
|
183
|
+
deleteLogs: (query: AuditLogQuery) => Promise<number>;
|
|
184
|
+
getLogStats: (ownerId: string) => Promise<AuditLogStats> | Promise<{
|
|
185
|
+
total: number;
|
|
186
|
+
byAction: {};
|
|
187
|
+
lastActivity: null;
|
|
188
|
+
}>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { DrizzleStore, createDrizzleStore };
|
|
192
|
+
export type { DrizzleAdapterConfig, DrizzleDB, DrizzleTable };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @donotlb/keypal v0.1.0
|
|
3
|
+
* A TypeScript library for secure API key management with cryptographic hashing, expiration, scopes, and pluggable storage
|
|
4
|
+
* © 2026 "donotlb" <donotlb@gmail.com>
|
|
5
|
+
* Released under the MIT License
|
|
6
|
+
* https://github.com/donotlb/keypal#readme
|
|
7
|
+
*/import{eq as g,and as p,arrayContains as w,or as N}from"drizzle-orm";import{g as O,l as I}from"../shared/keypal.lTVSZWgp.mjs";import{c as S,D as C}from"../shared/keypal.C-UeOmUF.mjs";import"nanoid";const v=f=>{const a={supportsDates:!0,supportsBooleans:!0,usePlural:!1,disableIdGeneration:!1,debugLogs:!1,...f.config},r={apiKeyTable:a.usePlural?"apikeys":"apikey",auditLogTable:a.usePlural?"auditlogs":"auditlog",flattenMetadata:!1,...f.schema},s=(...e)=>{if(!a.debugLogs)return;if(typeof a.debugLogs=="boolean"){I.info(`[${a.adapterName}]`,...e);return}const t=typeof e[0]=="string"?e[0]:null;t&&a.debugLogs[t]&&I.info(`[${a.adapterName}]`,...e)},m=e=>e==="apikey"?r.apiKeyTable:r.auditLogTable,n=(e,t)=>{if(e==="apikey"){const o=r.apiKeyColumns||{};if(o.metadataColumns&&t in o.metadataColumns)return o.metadataColumns[t]||t;if(t==="id")return o.id||"id";if(t==="keyHash")return o.keyHash||"keyHash";if(t==="metadata")return o.metadata||"metadata"}else return(r.auditLogColumns||{})[t]||t;return t},h={config:a,schema:r,debugLog:s,getColumnName:n,getTableName:m,transformApiKeyInput:e=>{const t={};if(r.flattenMetadata){t[n("apikey","id")]=e.id,t[n("apikey","keyHash")]=e.keyHash;for(const[o,i]of Object.entries(e.metadata)){const u=n("apikey",o);i==null?t[u]=null:typeof i=="object"?a.supportsJSON?t[u]=i:t[u]=JSON.stringify(i):i&&typeof i=="object"&&"toISOString"in i?a.supportsDates?t[u]=i:t[u]=i.toISOString():typeof i=="boolean"?a.supportsBooleans?t[u]=i:t[u]=i?1:0:t[u]=i}}else t[n("apikey","id")]=e.id,t[n("apikey","keyHash")]=e.keyHash,a.supportsJSON?t[n("apikey","metadata")]=e.metadata:t[n("apikey","metadata")]=JSON.stringify(e.metadata);return t},transformApiKeyOutput:e=>{const t=String(e[n("apikey","id")]),o=String(e[n("apikey","keyHash")]);let i;if(r.flattenMetadata){i={};const u=["ownerId","name","description","scopes","resources","rateLimit","expiresAt","revokedAt","lastUsedAt","createdAt","tags","allowedIps","allowedOrigins"];for(const d of u){const y=n("apikey",d),c=e[y];c!=null&&(d==="expiresAt"||d==="revokedAt"||d==="lastUsedAt"||d==="createdAt"?typeof c=="string"&&!a.supportsDates?i[d]=new Date(c):i[d]=c:d==="scopes"||d==="resources"||d==="tags"||d==="allowedIps"||d==="allowedOrigins"?typeof c=="string"&&!a.supportsJSON?i[d]=JSON.parse(c):i[d]=c:d==="rateLimit"&&typeof c=="string"&&!a.supportsJSON?i[d]=JSON.parse(c):i[d]=c)}}else{const u=e[n("apikey","metadata")];typeof u=="string"?i=JSON.parse(u):i=u}return{id:t,keyHash:o,metadata:i}},transformAuditLogInput:e=>{const t={};if(t[n("auditlog","id")]=e.id,t[n("auditlog","keyId")]=e.keyId,t[n("auditlog","ownerId")]=e.ownerId,t[n("auditlog","action")]=e.action,t[n("auditlog","timestamp")]=e.timestamp,e.data){const o=n("auditlog","data");a.supportsJSON?t[o]=e.data:t[o]=JSON.stringify(e.data)}return t},transformAuditLogOutput:e=>{const t={id:String(e[n("auditlog","id")]),keyId:String(e[n("auditlog","keyId")]),ownerId:String(e[n("auditlog","ownerId")]),action:e[n("auditlog","action")],timestamp:String(e[n("auditlog","timestamp")])},o=e[n("auditlog","data")];return o&&(typeof o=="string"?t.data=JSON.parse(o):t.data=o),t}},l=f.adapter(h);return{async save(e){s("save","Input:",e),!e.id&&!a.disableIdGeneration&&(e.id=a.customIdGenerator?a.customIdGenerator():O()),await l.save(e),s("save","Saved successfully")},async findByHash(e){s("findByHash","Hash:",e);const t=await l.findByHash(e);return s("findByHash","Result:",t),t},async findById(e){s("findById","ID:",e);const t=await l.findById(e);return s("findById","Result:",t),t},async findByOwner(e){s("findByOwner","Owner ID:",e);const t=await l.findByOwner(e);return s("findByOwner","Found:",t.length,"keys"),t},async findByTags(e,t){s("findByTags","Tags:",e,"Owner ID:",t);const o=await l.findByTags(e,t);return s("findByTags","Found:",o.length,"keys"),o},async findByTag(e,t){s("findByTag","Tag:",e,"Owner ID:",t);const o=await l.findByTag(e,t);return s("findByTag","Found:",o.length,"keys"),o},async updateMetadata(e,t){s("updateMetadata","ID:",e,"Metadata:",t),await l.updateMetadata(e,t),s("updateMetadata","Updated successfully")},async delete(e){s("delete","ID:",e),await l.delete(e),s("delete","Deleted successfully")},async deleteByOwner(e){s("deleteByOwner","Owner ID:",e),await l.deleteByOwner(e),s("deleteByOwner","Deleted successfully")},...l.saveLog&&{async saveLog(e){s("saveLog","Log:",e),await l.saveLog(e),s("saveLog","Saved successfully")}},...l.findLogs&&{async findLogs(e){s("findLogs","Query:",e);const t=await l.findLogs(e);return s("findLogs","Found:",t.length,"logs"),t}},...l.countLogs&&{async countLogs(e){s("countLogs","Query:",e);const t=await l.countLogs(e);return s("countLogs","Count:",t),t}},...l.deleteLogs&&{async deleteLogs(e){s("deleteLogs","Query:",e);const t=await l.deleteLogs(e);return s("deleteLogs","Deleted:",t,"logs"),t}},...l.getLogStats&&{async getLogStats(e){s("getLogStats","Owner ID:",e);const t=await l.getLogStats(e);return s("getLogStats","Stats:",t),t}}}};function b(f){const a=f.constructor?.name||"";return a.includes("Pg")||a.includes("Postgres")?"pg":a.includes("MySQL")||a.includes("Maria")?"mysql":a.includes("SQLite")||a.includes("Sqlite")?"sqlite":(I.warn("[Drizzle Store] Could not detect database provider, defaulting to PostgreSQL"),"pg")}function k(f){const{db:a,table:r,auditLogTable:s}=f,m=f.provider??b(a);return m==="mysql"&&s&&I.warn("[Drizzle Store] MySQL detected: RETURNING clause not supported, using fallback queries"),v({config:{adapterId:"drizzle",adapterName:"Drizzle ORM",supportsJSON:m!=="sqlite",supportsDates:!0,supportsBooleans:!0,debugLogs:f.debugLogs},schema:f.schema,adapter:n=>{const{transformApiKeyInput:h,transformApiKeyOutput:l}=n;return{async save(e){const t=h(e);await a.insert(r).values(t)},async findByHash(e){const t=n.getColumnName("apikey","keyHash"),o=await a.select().from(r).where(g(r[t],e)).limit(1);return o.length>0&&o[0]?l(o[0]):null},async findById(e){const t=n.getColumnName("apikey","id"),o=await a.select().from(r).where(g(r[t],e)).limit(1);return o.length>0&&o[0]?l(o[0]):null},async findByOwner(e){let t;if(n.schema.flattenMetadata){const o=n.getColumnName("apikey","ownerId");t=await a.select().from(r).where(g(r[o],e))}else{const o=n.getColumnName("apikey","metadata");t=await a.select().from(r).where(w(r[o],{ownerId:e}))}return t.map(l)},async findByTags(e,t){const o=[];if(n.schema.flattenMetadata){const i=n.getColumnName("apikey","tags"),u=e.map(d=>d.toLowerCase());for(const d of u)o.push(w(r[i],[d]));if(t!==void 0){const d=n.getColumnName("apikey","ownerId");o.push(g(r[d],t))}}else{const i=n.getColumnName("apikey","metadata");if(e.length>0){const u=e.map(d=>d.toLowerCase()).map(d=>w(r[i],{tags:[d]}));o.push(N(...u))}t!==void 0&&o.push(w(r[i],{ownerId:t}))}return o.length===0?[]:(await a.select().from(r).where(p(...o))).map(l)},async findByTag(e,t){return this.findByTags([e],t)},async updateMetadata(e,t){const o=await this.findById(e);if(!o)throw new Error(`API key with id ${e} not found`);const i={...o.metadata,...t},u={...o,metadata:i},d=h(u),y=n.getColumnName("apikey","id");if(n.schema.flattenMetadata){const c={};for(const[B]of Object.entries(t)){const L=n.getColumnName("apikey",B);c[L]=d[L]}await a.update(r).set(c).where(g(r[y],e))}else{const c=n.getColumnName("apikey","metadata");await a.update(r).set({[c]:i}).where(g(r[y],e))}},async delete(e){const t=n.getColumnName("apikey","id");await a.delete(r).where(g(r[t],e))},async deleteByOwner(e){if(n.schema.flattenMetadata){const t=n.getColumnName("apikey","ownerId");await a.delete(r).where(g(r[t],e))}else{const t=n.getColumnName("apikey","metadata");await a.delete(r).where(w(r[t],{ownerId:e}))}},...s&&n.transformAuditLogInput&&{async saveLog(e){const t=n.transformAuditLogInput(e);await a.insert(s).values(t)},async findLogs(e){const t=[];if(e.keyId){const y=n.getColumnName("auditlog","keyId");t.push(g(s[y],e.keyId))}if(e.ownerId){const y=n.getColumnName("auditlog","ownerId");t.push(g(s[y],e.ownerId))}if(e.action){const y=n.getColumnName("auditlog","action");t.push(g(s[y],e.action))}let o=a.select().from(s);t.length>0&&(o=o.where(p(...t)));const i=e.offset??0,u=e.limit??C,d=n.getColumnName("auditlog","timestamp");return(await o.orderBy(s[d]).limit(u).offset(i)).map(y=>n.transformAuditLogOutput?.(y))},async countLogs(e){const t=[];if(e.keyId){const i=n.getColumnName("auditlog","keyId");t.push(g(s[i],e.keyId))}if(e.ownerId){const i=n.getColumnName("auditlog","ownerId");t.push(g(s[i],e.ownerId))}if(e.action){const i=n.getColumnName("auditlog","action");t.push(g(s[i],e.action))}let o=a.select().from(s);return t.length>0&&(o=o.where(p(...t))),(await o).length},async deleteLogs(e){const t=[];if(e.keyId){const i=n.getColumnName("auditlog","keyId");t.push(g(s[i],e.keyId))}if(e.ownerId){const i=n.getColumnName("auditlog","ownerId");t.push(g(s[i],e.ownerId))}if(e.action){const i=n.getColumnName("auditlog","action");t.push(g(s[i],e.action))}if(t.length===0)return 0;if(m==="mysql"){const i=await a.select().from(s).where(p(...t));return await a.delete(s).where(p(...t)),i.length}const o=await a.delete(s).where(p(...t)).returning();return Array.isArray(o)?o.length:0},async getLogStats(e){const t=n.getColumnName("auditlog","ownerId"),o=(await a.select().from(s).where(g(s[t],e))).map(i=>n.transformAuditLogOutput?.(i));return S(o)}}}}})}class A{storage;constructor(a){this.storage=k(a)}save=a=>this.storage.save(a);findByHash=a=>this.storage.findByHash(a);findById=a=>this.storage.findById(a);findByOwner=a=>this.storage.findByOwner(a);findByTags=(a,r)=>this.storage.findByTags(a,r);findByTag=(a,r)=>this.storage.findByTag(a,r);updateMetadata=(a,r)=>this.storage.updateMetadata(a,r);delete=a=>this.storage.delete(a);deleteByOwner=a=>this.storage.deleteByOwner(a);saveLog=a=>this.storage.saveLog?.(a)??Promise.resolve();findLogs=a=>this.storage.findLogs?.(a)??Promise.resolve([]);countLogs=a=>this.storage.countLogs?.(a)??Promise.resolve(0);deleteLogs=a=>this.storage.deleteLogs?.(a)??Promise.resolve(0);getLogStats=a=>this.storage.getLogStats?.(a)??Promise.resolve({total:0,byAction:{},lastActivity:null})}export{A as DrizzleStore,k as createDrizzleStore};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { S as Storage, a as ApiKeyRecord, b as ApiKeyMetadata, d as AuditLog, c as AuditLogQuery, e as AuditLogStats } from '../shared/keypal.kItV-5pB.mjs';
|
|
2
|
+
import 'typebox';
|
|
3
|
+
|
|
4
|
+
declare class MemoryStore implements Storage {
|
|
5
|
+
private readonly keys;
|
|
6
|
+
private readonly hashIndex;
|
|
7
|
+
private readonly ownerIndex;
|
|
8
|
+
private readonly tagIndex;
|
|
9
|
+
private readonly logs;
|
|
10
|
+
save(record: ApiKeyRecord): Promise<void>;
|
|
11
|
+
findByHash(keyHash: string): Promise<ApiKeyRecord | null>;
|
|
12
|
+
findById(id: string): Promise<ApiKeyRecord | null>;
|
|
13
|
+
findByOwner(ownerId: string): Promise<ApiKeyRecord[]>;
|
|
14
|
+
findByTags(tags: string[], ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
15
|
+
findByTag(tag: string, ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
16
|
+
updateMetadata(id: string, metadata: Partial<ApiKeyMetadata>): Promise<void>;
|
|
17
|
+
delete(id: string): Promise<void>;
|
|
18
|
+
deleteByOwner(ownerId: string): Promise<void>;
|
|
19
|
+
saveLog(log: AuditLog): Promise<void>;
|
|
20
|
+
findLogs(query: AuditLogQuery): Promise<AuditLog[]>;
|
|
21
|
+
countLogs(query: AuditLogQuery): Promise<number>;
|
|
22
|
+
deleteLogs(query: AuditLogQuery): Promise<number>;
|
|
23
|
+
getLogStats(ownerId: string): Promise<AuditLogStats>;
|
|
24
|
+
private filterLogs;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { MemoryStore };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { S as Storage, a as ApiKeyRecord, b as ApiKeyMetadata, d as AuditLog, c as AuditLogQuery, e as AuditLogStats } from '../shared/keypal.kItV-5pB.js';
|
|
2
|
+
import 'typebox';
|
|
3
|
+
|
|
4
|
+
declare class MemoryStore implements Storage {
|
|
5
|
+
private readonly keys;
|
|
6
|
+
private readonly hashIndex;
|
|
7
|
+
private readonly ownerIndex;
|
|
8
|
+
private readonly tagIndex;
|
|
9
|
+
private readonly logs;
|
|
10
|
+
save(record: ApiKeyRecord): Promise<void>;
|
|
11
|
+
findByHash(keyHash: string): Promise<ApiKeyRecord | null>;
|
|
12
|
+
findById(id: string): Promise<ApiKeyRecord | null>;
|
|
13
|
+
findByOwner(ownerId: string): Promise<ApiKeyRecord[]>;
|
|
14
|
+
findByTags(tags: string[], ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
15
|
+
findByTag(tag: string, ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
16
|
+
updateMetadata(id: string, metadata: Partial<ApiKeyMetadata>): Promise<void>;
|
|
17
|
+
delete(id: string): Promise<void>;
|
|
18
|
+
deleteByOwner(ownerId: string): Promise<void>;
|
|
19
|
+
saveLog(log: AuditLog): Promise<void>;
|
|
20
|
+
findLogs(query: AuditLogQuery): Promise<AuditLog[]>;
|
|
21
|
+
countLogs(query: AuditLogQuery): Promise<number>;
|
|
22
|
+
deleteLogs(query: AuditLogQuery): Promise<number>;
|
|
23
|
+
getLogStats(ownerId: string): Promise<AuditLogStats>;
|
|
24
|
+
private filterLogs;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { MemoryStore };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @donotlb/keypal v0.1.0
|
|
3
|
+
* A TypeScript library for secure API key management with cryptographic hashing, expiration, scopes, and pluggable storage
|
|
4
|
+
* © 2026 "donotlb" <donotlb@gmail.com>
|
|
5
|
+
* Released under the MIT License
|
|
6
|
+
* https://github.com/donotlb/keypal#readme
|
|
7
|
+
*/import{D as h,c}from"../shared/keypal.C-UeOmUF.mjs";class f{keys=new Map;hashIndex=new Map;ownerIndex=new Map;tagIndex=new Map;logs=new Map;async save(t){if(this.keys.has(t.id))throw new Error(`API key with id ${t.id} already exists`);if(this.hashIndex.has(t.keyHash))throw new Error("API key hash collision detected");this.keys.set(t.id,t),this.hashIndex.set(t.keyHash,t.id);const{ownerId:e,tags:s}=t.metadata;if(e){const n=this.ownerIndex.get(e)??new Set;n.add(t.id),this.ownerIndex.set(e,n)}if(s)for(const n of s){const o=this.tagIndex.get(n)??new Set;o.add(t.id),this.tagIndex.set(n,o)}}async findByHash(t){const e=this.hashIndex.get(t);return e?this.keys.get(e)??null:null}async findById(t){return this.keys.get(t)??null}async findByOwner(t){const e=this.ownerIndex.get(t);if(!e?.size)return[];const s=[];for(const n of e){const o=this.keys.get(n);o&&s.push(o)}return s}async findByTags(t,e){const s=t.map(a=>a.toLowerCase()),n=new Set;for(const a of s){const i=this.tagIndex.get(a);if(i)for(const d of i)n.add(d)}const o=[];for(const a of n){const i=this.keys.get(a);i&&(e===void 0||i.metadata.ownerId===e)&&o.push(i)}return o}async findByTag(t,e){const s=t.toLowerCase(),n=this.tagIndex.get(s);if(!n?.size)return[];const o=[];for(const a of n){const i=this.keys.get(a);i&&(e===void 0||i.metadata.ownerId===e)&&o.push(i)}return o}async updateMetadata(t,e){const s=this.keys.get(t);if(!s)throw new Error(`API key with id ${t} not found`);const n=s.metadata.ownerId,o=s.metadata.tags;s.metadata={...s.metadata,...e};const a=s.metadata.ownerId,i=s.metadata.tags;if(n!==a){if(n){const d=this.ownerIndex.get(n);d?.delete(t),d?.size===0&&this.ownerIndex.delete(n)}if(a){const d=this.ownerIndex.get(a)??new Set;d.add(t),this.ownerIndex.set(a,d)}}if(e.tags!==void 0){if(o)for(const d of o){const r=this.tagIndex.get(d);r?.delete(t),r?.size===0&&this.tagIndex.delete(d)}if(i)for(const d of i){const r=this.tagIndex.get(d)??new Set;r.add(t),this.tagIndex.set(d,r)}}}async delete(t){const e=this.keys.get(t);if(e){this.hashIndex.delete(e.keyHash);const{ownerId:s,tags:n}=e.metadata;if(s){const o=this.ownerIndex.get(s);o?.delete(t),o?.size===0&&this.ownerIndex.delete(s)}if(n)for(const o of n){const a=this.tagIndex.get(o);a?.delete(t),a?.size===0&&this.tagIndex.delete(o)}}this.keys.delete(t)}async deleteByOwner(t){const e=this.ownerIndex.get(t);if(e){for(const s of e){const n=this.keys.get(s);if(n){if(this.hashIndex.delete(n.keyHash),n.metadata.tags)for(const o of n.metadata.tags){const a=this.tagIndex.get(o);a?.delete(s),a?.size===0&&this.tagIndex.delete(o)}this.keys.delete(s)}}this.ownerIndex.delete(t)}}async saveLog(t){this.logs.set(t.id,t)}async findLogs(t){const e=this.filterLogs(t);e.sort((o,a)=>a.timestamp.localeCompare(o.timestamp));const s=t.offset??0,n=t.limit??h;return e.slice(s,s+n)}async countLogs(t){return this.filterLogs(t).length}async deleteLogs(t){const e=this.filterLogs(t);for(const s of e)this.logs.delete(s.id);return e.length}async getLogStats(t){const e=Array.from(this.logs.values()).filter(s=>s.ownerId===t);return c(e)}filterLogs(t){let e=Array.from(this.logs.values());return t.keyId&&(e=e.filter(s=>s.keyId===t.keyId)),t.ownerId&&(e=e.filter(s=>s.ownerId===t.ownerId)),t.action&&(e=e.filter(s=>s.action===t.action)),t.startDate&&(e=e.filter(s=>s.timestamp>=t.startDate)),t.endDate&&(e=e.filter(s=>s.timestamp<=t.endDate)),e}}export{f as MemoryStore};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import { S as Storage, a as ApiKeyRecord, b as ApiKeyMetadata, d as AuditLog, c as AuditLogQuery, e as AuditLogStats } from '../shared/keypal.kItV-5pB.mjs';
|
|
3
|
+
import 'typebox';
|
|
4
|
+
|
|
5
|
+
declare class RedisStore implements Storage {
|
|
6
|
+
private readonly redis;
|
|
7
|
+
private readonly prefix;
|
|
8
|
+
constructor(options: {
|
|
9
|
+
client: Redis;
|
|
10
|
+
prefix?: string;
|
|
11
|
+
});
|
|
12
|
+
private key;
|
|
13
|
+
private tagKey;
|
|
14
|
+
private hashKey;
|
|
15
|
+
private ownerKey;
|
|
16
|
+
private logKey;
|
|
17
|
+
private logsByKeyIndex;
|
|
18
|
+
private logsByOwnerIndex;
|
|
19
|
+
private logsByActionIndex;
|
|
20
|
+
private allLogsIndex;
|
|
21
|
+
save(record: ApiKeyRecord): Promise<void>;
|
|
22
|
+
findByHash(keyHash: string): Promise<ApiKeyRecord | null>;
|
|
23
|
+
findById(id: string): Promise<ApiKeyRecord | null>;
|
|
24
|
+
findByOwner(ownerId: string): Promise<ApiKeyRecord[]>;
|
|
25
|
+
findByTags(tags: string[], ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
26
|
+
findByTag(tag: string, ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
27
|
+
updateMetadata(id: string, metadata: Partial<ApiKeyMetadata>): Promise<void>;
|
|
28
|
+
delete(id: string): Promise<void>;
|
|
29
|
+
deleteByOwner(ownerId: string): Promise<void>;
|
|
30
|
+
setTtl(id: string, ttlSeconds: number): Promise<void>;
|
|
31
|
+
saveLog(log: AuditLog): Promise<void>;
|
|
32
|
+
findLogs(query: AuditLogQuery): Promise<AuditLog[]>;
|
|
33
|
+
countLogs(query: AuditLogQuery): Promise<number>;
|
|
34
|
+
deleteLogs(query: AuditLogQuery): Promise<number>;
|
|
35
|
+
getLogStats(ownerId: string): Promise<AuditLogStats>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a log matches all query criteria
|
|
38
|
+
*/
|
|
39
|
+
private matchesQuery;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { RedisStore };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import { S as Storage, a as ApiKeyRecord, b as ApiKeyMetadata, d as AuditLog, c as AuditLogQuery, e as AuditLogStats } from '../shared/keypal.kItV-5pB.js';
|
|
3
|
+
import 'typebox';
|
|
4
|
+
|
|
5
|
+
declare class RedisStore implements Storage {
|
|
6
|
+
private readonly redis;
|
|
7
|
+
private readonly prefix;
|
|
8
|
+
constructor(options: {
|
|
9
|
+
client: Redis;
|
|
10
|
+
prefix?: string;
|
|
11
|
+
});
|
|
12
|
+
private key;
|
|
13
|
+
private tagKey;
|
|
14
|
+
private hashKey;
|
|
15
|
+
private ownerKey;
|
|
16
|
+
private logKey;
|
|
17
|
+
private logsByKeyIndex;
|
|
18
|
+
private logsByOwnerIndex;
|
|
19
|
+
private logsByActionIndex;
|
|
20
|
+
private allLogsIndex;
|
|
21
|
+
save(record: ApiKeyRecord): Promise<void>;
|
|
22
|
+
findByHash(keyHash: string): Promise<ApiKeyRecord | null>;
|
|
23
|
+
findById(id: string): Promise<ApiKeyRecord | null>;
|
|
24
|
+
findByOwner(ownerId: string): Promise<ApiKeyRecord[]>;
|
|
25
|
+
findByTags(tags: string[], ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
26
|
+
findByTag(tag: string, ownerId?: string): Promise<ApiKeyRecord[]>;
|
|
27
|
+
updateMetadata(id: string, metadata: Partial<ApiKeyMetadata>): Promise<void>;
|
|
28
|
+
delete(id: string): Promise<void>;
|
|
29
|
+
deleteByOwner(ownerId: string): Promise<void>;
|
|
30
|
+
setTtl(id: string, ttlSeconds: number): Promise<void>;
|
|
31
|
+
saveLog(log: AuditLog): Promise<void>;
|
|
32
|
+
findLogs(query: AuditLogQuery): Promise<AuditLog[]>;
|
|
33
|
+
countLogs(query: AuditLogQuery): Promise<number>;
|
|
34
|
+
deleteLogs(query: AuditLogQuery): Promise<number>;
|
|
35
|
+
getLogStats(ownerId: string): Promise<AuditLogStats>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a log matches all query criteria
|
|
38
|
+
*/
|
|
39
|
+
private matchesQuery;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { RedisStore };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @donotlb/keypal v0.1.0
|
|
3
|
+
* A TypeScript library for secure API key management with cryptographic hashing, expiration, scopes, and pluggable storage
|
|
4
|
+
* © 2026 "donotlb" <donotlb@gmail.com>
|
|
5
|
+
* Released under the MIT License
|
|
6
|
+
* https://github.com/donotlb/keypal#readme
|
|
7
|
+
*/import{D as w,c as u}from"../shared/keypal.C-UeOmUF.mjs";function p(h){if(typeof h!="object"||h===null)return!1;const t=h;return typeof t.id=="string"&&typeof t.keyHash=="string"&&typeof t.metadata=="object"&&t.metadata!==null}function I(h){if(typeof h!="object"||h===null)return!1;const t=h;return typeof t.id=="string"&&typeof t.keyId=="string"&&typeof t.ownerId=="string"&&typeof t.action=="string"&&typeof t.timestamp=="string"}async function y(h,t){const e=await h.exec();if(!e)throw new Error(`Redis pipeline returned null for ${t}`);for(let s=0;s<e.length;s++){const i=e[s];if(i?.[0])throw new Error(`Redis pipeline command ${s} failed in ${t}: ${i[0].message}`)}return e}function l(h){try{const t=JSON.parse(h);return p(t)?t:null}catch{return null}}function m(h){try{const t=JSON.parse(h);return I(t)?t:null}catch{return null}}class x{redis;prefix;constructor(t){this.redis=t.client,this.prefix=t.prefix??"apikey:"}key(t){return`${this.prefix}${t}`}tagKey(t){return`${this.prefix}tag:${t}`}hashKey(t){return`${this.prefix}hash:${t}`}ownerKey(t){return`${this.prefix}owner:${t}`}logKey(t){return`${this.prefix}log:${t}`}logsByKeyIndex(t){return`${this.prefix}logs:key:${t}`}logsByOwnerIndex(t){return`${this.prefix}logs:owner:${t}`}logsByActionIndex(t){return`${this.prefix}logs:action:${t}`}allLogsIndex(){return`${this.prefix}logs:all`}async save(t){if(await this.findById(t.id))throw new Error(`API key with id ${t.id} already exists`);const e=this.redis.pipeline();if(e.set(this.key(t.id),JSON.stringify(t)),e.set(this.hashKey(t.keyHash),t.id),e.sadd(this.ownerKey(t.metadata.ownerId),t.id),t.metadata.tags?.length)for(const s of t.metadata.tags)e.sadd(this.tagKey(s.toLowerCase()),t.id);await y(e,"save")}async findByHash(t){const e=await this.redis.get(this.hashKey(t));return e?this.findById(e):null}async findById(t){const e=await this.redis.get(this.key(t));return e?l(e):null}async findByOwner(t){const e=await this.redis.smembers(this.ownerKey(t));if(!e.length)return[];const s=this.redis.pipeline();for(const n of e)s.get(this.key(n));const i=await y(s,"findByOwner"),o=[];for(const n of i)if(n[1]){const d=l(n[1]);d&&o.push(d)}return o}async findByTags(t,e){const s=t.map(r=>this.tagKey(r.toLowerCase()));if(!s.length)return[];let i=s.length===1&&s[0]?await this.redis.smembers(s[0]):await this.redis.sunion(...s);if(e!==void 0&&i.length){const r=await this.redis.smembers(this.ownerKey(e));i=i.filter(a=>r.includes(a))}if(!i.length)return[];const o=this.redis.pipeline();for(const r of i)o.get(this.key(r));const n=await y(o,"findByTags"),d=[];for(const r of n)if(r[1]){const a=l(r[1]);a&&d.push(a)}return d}async findByTag(t,e){return this.findByTags([t],e)}async updateMetadata(t,e){const s=await this.findById(t);if(!s)throw new Error(`API key with id ${t} not found`);const i=s.metadata.tags??[];s.metadata={...s.metadata,...e};const o=s.metadata.tags??[],n=this.redis.pipeline();if(n.set(this.key(t),JSON.stringify(s)),e.revokedAt&&n.del(this.hashKey(s.keyHash)),e.tags!==void 0){const d=new Set(i.map(a=>a.toLowerCase())),r=new Set(o.map(a=>a.toLowerCase()));for(const a of d)r.has(a)||n.srem(this.tagKey(a),t);for(const a of r)d.has(a)||n.sadd(this.tagKey(a),t)}await y(n,"updateMetadata")}async delete(t){const e=await this.findById(t);if(!e)return;const s=this.redis.pipeline();if(s.del(this.key(t)),s.del(this.hashKey(e.keyHash)),s.srem(this.ownerKey(e.metadata.ownerId),t),e.metadata.tags?.length)for(const i of e.metadata.tags)s.srem(this.tagKey(i.toLowerCase()),t);await y(s,"delete")}async deleteByOwner(t){const e=await this.redis.smembers(this.ownerKey(t));if(!e.length)return;const s=this.redis.pipeline();for(const n of e)s.get(this.key(n));const i=await y(s,"deleteByOwner:fetch"),o=this.redis.pipeline();for(let n=0;n<e.length;n++){const d=e[n];if(!d)continue;o.del(this.key(d));const r=i[n];if(r?.[1]){const a=l(r[1]);if(a&&(o.del(this.hashKey(a.keyHash)),a.metadata.tags?.length))for(const f of a.metadata.tags)o.srem(this.tagKey(f.toLowerCase()),d)}}o.del(this.ownerKey(t)),await y(o,"deleteByOwner:delete")}async setTtl(t,e){const s=await this.findById(t);if(!s)return;const i=this.redis.pipeline();i.expire(this.key(t),e),i.expire(this.hashKey(s.keyHash),e),await y(i,"setTtl")}async saveLog(t){const e=new Date(t.timestamp).getTime(),s=this.redis.pipeline();s.set(this.logKey(t.id),JSON.stringify(t)),s.zadd(this.allLogsIndex(),e,t.id),s.zadd(this.logsByKeyIndex(t.keyId),e,t.id),s.zadd(this.logsByOwnerIndex(t.ownerId),e,t.id),s.zadd(this.logsByActionIndex(t.action),e,t.id),await y(s,"saveLog")}async findLogs(t){const e=t.offset??0,s=t.limit??w;let i;t.keyId?i=this.logsByKeyIndex(t.keyId):t.ownerId?i=this.logsByOwnerIndex(t.ownerId):t.action?i=this.logsByActionIndex(t.action):i=this.allLogsIndex();const o=t.startDate?new Date(t.startDate).getTime():"-inf",n=t.endDate?new Date(t.endDate).getTime():"+inf",d=await this.redis.zrevrangebyscore(i,n,o,"LIMIT",e,s);if(!d.length)return[];const r=this.redis.pipeline();for(const c of d)r.get(this.logKey(c));const a=await y(r,"findLogs"),f=[];for(const c of a)if(c[1]){const g=m(c[1]);g&&this.matchesQuery(g,t)&&f.push(g)}return f}async countLogs(t){let e;t.keyId?e=this.logsByKeyIndex(t.keyId):t.ownerId?e=this.logsByOwnerIndex(t.ownerId):t.action?e=this.logsByActionIndex(t.action):e=this.allLogsIndex();const s=t.startDate?new Date(t.startDate).getTime():"-inf",i=t.endDate?new Date(t.endDate).getTime():"+inf";return[t.keyId,t.ownerId,t.action].filter(Boolean).length>1?(await this.findLogs({...t,limit:Number.MAX_SAFE_INTEGER,offset:0})).length:this.redis.zcount(e,s,i)}async deleteLogs(t){const e=await this.findLogs({...t,limit:Number.MAX_SAFE_INTEGER,offset:0});if(!e.length)return 0;const s=this.redis.pipeline();for(const i of e)s.del(this.logKey(i.id)),s.zrem(this.allLogsIndex(),i.id),s.zrem(this.logsByKeyIndex(i.keyId),i.id),s.zrem(this.logsByOwnerIndex(i.ownerId),i.id),s.zrem(this.logsByActionIndex(i.action),i.id);return await y(s,"deleteLogs"),e.length}async getLogStats(t){const e=await this.findLogs({ownerId:t,limit:Number.MAX_SAFE_INTEGER});return u(e)}matchesQuery(t,e){return!(e.keyId&&t.keyId!==e.keyId||e.ownerId&&t.ownerId!==e.ownerId||e.action&&t.action!==e.action||e.startDate&&t.timestamp<e.startDate||e.endDate&&t.timestamp>e.endDate)}}export{x as RedisStore};
|
package/package.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@donotlb/keypal",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A TypeScript library for secure API key management with cryptographic hashing, expiration, scopes, and pluggable storage",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./memory": {
|
|
14
|
+
"types": "./dist/storage/memory.d.ts",
|
|
15
|
+
"import": "./dist/storage/memory.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./redis": {
|
|
18
|
+
"types": "./dist/storage/redis.d.ts",
|
|
19
|
+
"import": "./dist/storage/redis.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./drizzle": {
|
|
22
|
+
"types": "./dist/storage/drizzle.d.ts",
|
|
23
|
+
"import": "./dist/storage/drizzle.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./drizzle/schema": {
|
|
26
|
+
"types": "./dist/drizzle/schema.d.ts",
|
|
27
|
+
"import": "./dist/drizzle/schema.mjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/donotlb/keypal.git"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/donotlb/keypal#readme",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/donotlb/keypal/issues"
|
|
37
|
+
},
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"author": "donotlb <donotlb@gmail.com>",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"keywords": [
|
|
49
|
+
"api-keys",
|
|
50
|
+
"api-key-management",
|
|
51
|
+
"security",
|
|
52
|
+
"authentication",
|
|
53
|
+
"authorization",
|
|
54
|
+
"typescript",
|
|
55
|
+
"keypal",
|
|
56
|
+
"hashing",
|
|
57
|
+
"scopes",
|
|
58
|
+
"permissions",
|
|
59
|
+
"redis",
|
|
60
|
+
"drizzle",
|
|
61
|
+
"storage"
|
|
62
|
+
],
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "unbuild",
|
|
65
|
+
"test": "vitest",
|
|
66
|
+
"test:ui": "vitest --ui",
|
|
67
|
+
"test:coverage": "vitest --coverage",
|
|
68
|
+
"test:redis": "vitest run src/storage/redis.test.ts",
|
|
69
|
+
"test:drizzle": "vitest run src/storage/drizzle.test.ts",
|
|
70
|
+
"bench": "bun run benchmark.ts",
|
|
71
|
+
"bench:core": "bun run benchmark-core.ts",
|
|
72
|
+
"bench:storage": "bun run benchmark-storage.ts",
|
|
73
|
+
"example:hono": "bun run examples/hono-api.ts",
|
|
74
|
+
"studio": "bunx drizzle-kit studio",
|
|
75
|
+
"db:generate": "bunx drizzle-kit generate",
|
|
76
|
+
"db:migrate": "bunx drizzle-kit migrate",
|
|
77
|
+
"db:push": "bunx drizzle-kit push",
|
|
78
|
+
"prepare": "bunx husky || true",
|
|
79
|
+
"prepublishOnly": "bun run build",
|
|
80
|
+
"version:patch": "npm version patch --no-git-tag-version",
|
|
81
|
+
"version:minor": "npm version minor --no-git-tag-version",
|
|
82
|
+
"version:major": "npm version major --no-git-tag-version"
|
|
83
|
+
},
|
|
84
|
+
"devDependencies": {
|
|
85
|
+
"@biomejs/biome": "^2.3.0",
|
|
86
|
+
"@hono/node-server": "^1.19.5",
|
|
87
|
+
"@types/bun": "latest",
|
|
88
|
+
"@types/pg": "^8.15.5",
|
|
89
|
+
"@vitest/coverage-v8": "^4.0.3",
|
|
90
|
+
"@vitest/ui": "^4.0.3",
|
|
91
|
+
"better-sqlite3": "^12.4.1",
|
|
92
|
+
"chalk": "^5.6.2",
|
|
93
|
+
"hono": "^4.10.2",
|
|
94
|
+
"husky": "^9.1.7",
|
|
95
|
+
"lint-staged": "^16.2.6",
|
|
96
|
+
"mitata": "^1.0.34",
|
|
97
|
+
"pg": "^8.16.3",
|
|
98
|
+
"ultracite": "^6.0.1",
|
|
99
|
+
"drizzle-kit": "^0.31.0",
|
|
100
|
+
"unbuild": "^3.6.1",
|
|
101
|
+
"undici": "^7.16.0",
|
|
102
|
+
"vitest": "^4.0.3"
|
|
103
|
+
},
|
|
104
|
+
"peerDependencies": {
|
|
105
|
+
"typescript": "^5",
|
|
106
|
+
"drizzle-orm": "^0.44.6",
|
|
107
|
+
"ioredis": "^5.8.2"
|
|
108
|
+
},
|
|
109
|
+
"peerDependenciesMeta": {
|
|
110
|
+
"drizzle-orm": { "optional": true },
|
|
111
|
+
"ioredis": { "optional": true }
|
|
112
|
+
},
|
|
113
|
+
"dependencies": {
|
|
114
|
+
"nanoid": "^5.1.6",
|
|
115
|
+
"typebox": "^1.0.43"
|
|
116
|
+
},
|
|
117
|
+
"lint-staged": {
|
|
118
|
+
"*.{js,jsx,ts,tsx,json,jsonc,css,scss,md,mdx}": [
|
|
119
|
+
"bun x ultracite fix"
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|