@twelvehart/supermemory-runtime 1.0.0-next.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.
Files changed (156) hide show
  1. package/.env.example +57 -0
  2. package/README.md +374 -0
  3. package/dist/index.js +189 -0
  4. package/dist/mcp/index.js +1132 -0
  5. package/docker-compose.prod.yml +91 -0
  6. package/docker-compose.yml +358 -0
  7. package/drizzle/0000_dapper_the_professor.sql +159 -0
  8. package/drizzle/0001_api_keys.sql +51 -0
  9. package/drizzle/meta/0000_snapshot.json +1532 -0
  10. package/drizzle/meta/_journal.json +13 -0
  11. package/drizzle.config.ts +20 -0
  12. package/package.json +114 -0
  13. package/scripts/add-extraction-job.ts +122 -0
  14. package/scripts/benchmark-pgvector.ts +122 -0
  15. package/scripts/bootstrap.sh +209 -0
  16. package/scripts/check-runtime-pack.ts +111 -0
  17. package/scripts/claude-mcp-config.ts +336 -0
  18. package/scripts/docker-entrypoint.sh +183 -0
  19. package/scripts/doctor.ts +377 -0
  20. package/scripts/init-db.sql +33 -0
  21. package/scripts/install.sh +1110 -0
  22. package/scripts/mcp-setup.ts +271 -0
  23. package/scripts/migrations/001_create_pgvector_extension.sql +31 -0
  24. package/scripts/migrations/002_create_memory_embeddings_table.sql +75 -0
  25. package/scripts/migrations/003_create_hnsw_index.sql +94 -0
  26. package/scripts/migrations/004_create_memory_embeddings_standalone.sql +70 -0
  27. package/scripts/migrations/005_create_chunks_table.sql +95 -0
  28. package/scripts/migrations/006_create_processing_queue.sql +45 -0
  29. package/scripts/migrations/generate_test_data.sql +42 -0
  30. package/scripts/migrations/phase1_comprehensive_test.sql +204 -0
  31. package/scripts/migrations/run_migrations.sh +286 -0
  32. package/scripts/migrations/test_hnsw_index.sql +255 -0
  33. package/scripts/pre-commit-secrets +282 -0
  34. package/scripts/run-extraction-worker.ts +46 -0
  35. package/scripts/run-phase1-tests.sh +291 -0
  36. package/scripts/setup.ts +222 -0
  37. package/scripts/smoke-install.sh +12 -0
  38. package/scripts/test-health-endpoint.sh +328 -0
  39. package/src/api/index.ts +2 -0
  40. package/src/api/middleware/auth.ts +80 -0
  41. package/src/api/middleware/csrf.ts +308 -0
  42. package/src/api/middleware/errorHandler.ts +166 -0
  43. package/src/api/middleware/rateLimit.ts +360 -0
  44. package/src/api/middleware/validation.ts +514 -0
  45. package/src/api/routes/documents.ts +286 -0
  46. package/src/api/routes/profiles.ts +237 -0
  47. package/src/api/routes/search.ts +71 -0
  48. package/src/api/stores/index.ts +58 -0
  49. package/src/config/bootstrap-env.ts +3 -0
  50. package/src/config/env.ts +71 -0
  51. package/src/config/feature-flags.ts +25 -0
  52. package/src/config/index.ts +140 -0
  53. package/src/config/secrets.config.ts +291 -0
  54. package/src/db/client.ts +92 -0
  55. package/src/db/index.ts +73 -0
  56. package/src/db/postgres.ts +72 -0
  57. package/src/db/schema/chunks.schema.ts +31 -0
  58. package/src/db/schema/containers.schema.ts +46 -0
  59. package/src/db/schema/documents.schema.ts +49 -0
  60. package/src/db/schema/embeddings.schema.ts +32 -0
  61. package/src/db/schema/index.ts +11 -0
  62. package/src/db/schema/memories.schema.ts +72 -0
  63. package/src/db/schema/profiles.schema.ts +34 -0
  64. package/src/db/schema/queue.schema.ts +59 -0
  65. package/src/db/schema/relationships.schema.ts +42 -0
  66. package/src/db/schema.ts +223 -0
  67. package/src/db/worker-connection.ts +47 -0
  68. package/src/index.ts +235 -0
  69. package/src/mcp/CLAUDE.md +1 -0
  70. package/src/mcp/index.ts +1380 -0
  71. package/src/mcp/legacyState.ts +22 -0
  72. package/src/mcp/rateLimit.ts +358 -0
  73. package/src/mcp/resources.ts +309 -0
  74. package/src/mcp/results.ts +104 -0
  75. package/src/mcp/tools.ts +401 -0
  76. package/src/queues/config.ts +119 -0
  77. package/src/queues/index.ts +289 -0
  78. package/src/sdk/client.ts +225 -0
  79. package/src/sdk/errors.ts +266 -0
  80. package/src/sdk/http.ts +560 -0
  81. package/src/sdk/index.ts +244 -0
  82. package/src/sdk/resources/base.ts +65 -0
  83. package/src/sdk/resources/connections.ts +204 -0
  84. package/src/sdk/resources/documents.ts +163 -0
  85. package/src/sdk/resources/index.ts +10 -0
  86. package/src/sdk/resources/memories.ts +150 -0
  87. package/src/sdk/resources/search.ts +60 -0
  88. package/src/sdk/resources/settings.ts +36 -0
  89. package/src/sdk/types.ts +674 -0
  90. package/src/services/chunking/index.ts +451 -0
  91. package/src/services/chunking.service.ts +650 -0
  92. package/src/services/csrf.service.ts +252 -0
  93. package/src/services/documents.repository.ts +219 -0
  94. package/src/services/documents.service.ts +191 -0
  95. package/src/services/embedding.service.ts +404 -0
  96. package/src/services/extraction.service.ts +300 -0
  97. package/src/services/extractors/code.extractor.ts +451 -0
  98. package/src/services/extractors/index.ts +9 -0
  99. package/src/services/extractors/markdown.extractor.ts +461 -0
  100. package/src/services/extractors/pdf.extractor.ts +315 -0
  101. package/src/services/extractors/text.extractor.ts +118 -0
  102. package/src/services/extractors/url.extractor.ts +243 -0
  103. package/src/services/index.ts +235 -0
  104. package/src/services/ingestion.service.ts +177 -0
  105. package/src/services/llm/anthropic.ts +400 -0
  106. package/src/services/llm/base.ts +460 -0
  107. package/src/services/llm/contradiction-detector.service.ts +526 -0
  108. package/src/services/llm/heuristics.ts +148 -0
  109. package/src/services/llm/index.ts +309 -0
  110. package/src/services/llm/memory-classifier.service.ts +383 -0
  111. package/src/services/llm/memory-extension-detector.service.ts +523 -0
  112. package/src/services/llm/mock.ts +470 -0
  113. package/src/services/llm/openai.ts +398 -0
  114. package/src/services/llm/prompts.ts +438 -0
  115. package/src/services/llm/types.ts +373 -0
  116. package/src/services/memory.repository.ts +1769 -0
  117. package/src/services/memory.service.ts +1338 -0
  118. package/src/services/memory.types.ts +234 -0
  119. package/src/services/persistence/index.ts +295 -0
  120. package/src/services/pipeline.service.ts +509 -0
  121. package/src/services/profile.repository.ts +436 -0
  122. package/src/services/profile.service.ts +560 -0
  123. package/src/services/profile.types.ts +270 -0
  124. package/src/services/relationships/detector.ts +1128 -0
  125. package/src/services/relationships/index.ts +268 -0
  126. package/src/services/relationships/memory-integration.ts +459 -0
  127. package/src/services/relationships/strategies.ts +132 -0
  128. package/src/services/relationships/types.ts +370 -0
  129. package/src/services/search.service.ts +761 -0
  130. package/src/services/search.types.ts +220 -0
  131. package/src/services/secrets.service.ts +384 -0
  132. package/src/services/vectorstore/base.ts +327 -0
  133. package/src/services/vectorstore/index.ts +444 -0
  134. package/src/services/vectorstore/memory.ts +286 -0
  135. package/src/services/vectorstore/migration.ts +295 -0
  136. package/src/services/vectorstore/mock.ts +403 -0
  137. package/src/services/vectorstore/pgvector.ts +695 -0
  138. package/src/services/vectorstore/types.ts +247 -0
  139. package/src/startup.ts +389 -0
  140. package/src/types/api.types.ts +193 -0
  141. package/src/types/document.types.ts +103 -0
  142. package/src/types/index.ts +241 -0
  143. package/src/types/profile.base.ts +133 -0
  144. package/src/utils/errors.ts +447 -0
  145. package/src/utils/id.ts +15 -0
  146. package/src/utils/index.ts +101 -0
  147. package/src/utils/logger.ts +313 -0
  148. package/src/utils/sanitization.ts +501 -0
  149. package/src/utils/secret-validation.ts +273 -0
  150. package/src/utils/synonyms.ts +188 -0
  151. package/src/utils/validation.ts +581 -0
  152. package/src/workers/chunking.worker.ts +242 -0
  153. package/src/workers/embedding.worker.ts +358 -0
  154. package/src/workers/extraction.worker.ts +346 -0
  155. package/src/workers/indexing.worker.ts +505 -0
  156. package/tsconfig.json +38 -0
@@ -0,0 +1,289 @@
1
+ /**
2
+ * BullMQ Queue Factory and Exports
3
+ *
4
+ * Provides queue instances for document processing pipeline:
5
+ * - extraction: Document content extraction
6
+ * - chunking: Text chunking for embeddings
7
+ * - embedding: Generate vector embeddings
8
+ * - indexing: Index into vector store
9
+ */
10
+
11
+ import { Queue, QueueEvents } from 'bullmq'
12
+ import { Redis } from 'ioredis'
13
+ import { defaultQueueOptions, deadLetterQueueOptions, redisConfig, JobProgress } from './config.js'
14
+ import { ValidationError, NotFoundError, ErrorCode } from '../utils/errors.js'
15
+ import { getLogger } from '../utils/logger.js'
16
+
17
+ const logger = getLogger('queues')
18
+
19
+ /**
20
+ * Queue names enum for type safety
21
+ */
22
+ export enum QueueName {
23
+ EXTRACTION = 'extraction',
24
+ CHUNKING = 'chunking',
25
+ EMBEDDING = 'embedding',
26
+ INDEXING = 'indexing',
27
+ DEAD_LETTER = 'dead-letter',
28
+ }
29
+
30
+ /**
31
+ * Queue metrics interface
32
+ */
33
+ export interface QueueMetrics {
34
+ waiting: number
35
+ active: number
36
+ completed: number
37
+ failed: number
38
+ delayed: number
39
+ paused: boolean
40
+ }
41
+
42
+ /**
43
+ * Redis connection singleton
44
+ */
45
+ let redisConnection: Redis | null = null
46
+
47
+ /**
48
+ * Get or create Redis connection
49
+ */
50
+ export function getRedisConnection(): Redis {
51
+ if (!redisConnection) {
52
+ redisConnection = new Redis(redisConfig.connection)
53
+
54
+ redisConnection.on('connect', () => {
55
+ logger.info('Redis connected successfully')
56
+ })
57
+
58
+ redisConnection.on('ready', () => {
59
+ logger.info('Redis ready to accept commands')
60
+ })
61
+
62
+ redisConnection.on('error', (err: Error) => {
63
+ logger.error('Redis connection error', {}, err)
64
+ })
65
+
66
+ redisConnection.on('close', () => {
67
+ logger.info('Redis connection closed')
68
+ })
69
+
70
+ redisConnection.on('reconnecting', () => {
71
+ logger.info('Redis attempting to reconnect')
72
+ })
73
+ }
74
+
75
+ return redisConnection
76
+ }
77
+
78
+ /**
79
+ * Check Redis connection health
80
+ */
81
+ export async function checkRedisHealth(): Promise<boolean> {
82
+ try {
83
+ const redis = getRedisConnection()
84
+ const result = await redis.ping()
85
+ return result === 'PONG'
86
+ } catch (error) {
87
+ logger.error('Redis health check failed', {}, error instanceof Error ? error : undefined)
88
+ return false
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Close Redis connection gracefully
94
+ */
95
+ export async function closeRedisConnection(): Promise<void> {
96
+ if (redisConnection) {
97
+ await redisConnection.quit()
98
+ redisConnection = null
99
+ logger.info('Redis connection closed gracefully')
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Create a queue instance
105
+ */
106
+ function createQueue(name: QueueName, isDLQ = false): Queue {
107
+ const options = isDLQ ? deadLetterQueueOptions : defaultQueueOptions
108
+ return new Queue(name, options)
109
+ }
110
+
111
+ /**
112
+ * Queue instances
113
+ */
114
+ export const extractionQueue = createQueue(QueueName.EXTRACTION)
115
+ export const chunkingQueue = createQueue(QueueName.CHUNKING)
116
+ export const embeddingQueue = createQueue(QueueName.EMBEDDING)
117
+ export const indexingQueue = createQueue(QueueName.INDEXING)
118
+ export const deadLetterQueue = createQueue(QueueName.DEAD_LETTER, true)
119
+
120
+ /**
121
+ * Queue events for monitoring
122
+ */
123
+ export const extractionEvents = new QueueEvents(QueueName.EXTRACTION, {
124
+ connection: redisConfig.connection,
125
+ })
126
+ export const chunkingEvents = new QueueEvents(QueueName.CHUNKING, {
127
+ connection: redisConfig.connection,
128
+ })
129
+ export const embeddingEvents = new QueueEvents(QueueName.EMBEDDING, {
130
+ connection: redisConfig.connection,
131
+ })
132
+ export const indexingEvents = new QueueEvents(QueueName.INDEXING, {
133
+ connection: redisConfig.connection,
134
+ })
135
+
136
+ /**
137
+ * Get queue by name
138
+ */
139
+ export function getQueue(name: QueueName): Queue {
140
+ switch (name) {
141
+ case QueueName.EXTRACTION:
142
+ return extractionQueue
143
+ case QueueName.CHUNKING:
144
+ return chunkingQueue
145
+ case QueueName.EMBEDDING:
146
+ return embeddingQueue
147
+ case QueueName.INDEXING:
148
+ return indexingQueue
149
+ case QueueName.DEAD_LETTER:
150
+ return deadLetterQueue
151
+ default:
152
+ throw new ValidationError(`Unknown queue: ${name}`, {
153
+ queue: [`Invalid queue '${name}'. Valid queues: ${Object.values(QueueName).join(', ')}`],
154
+ })
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Get queue metrics for monitoring
160
+ */
161
+ export async function getQueueMetrics(queueName: QueueName): Promise<QueueMetrics> {
162
+ const queue = getQueue(queueName)
163
+
164
+ const [waiting, active, completed, failed, delayed, isPaused] = await Promise.all([
165
+ queue.getWaitingCount(),
166
+ queue.getActiveCount(),
167
+ queue.getCompletedCount(),
168
+ queue.getFailedCount(),
169
+ queue.getDelayedCount(),
170
+ queue.isPaused(),
171
+ ])
172
+
173
+ return {
174
+ waiting,
175
+ active,
176
+ completed,
177
+ failed,
178
+ delayed,
179
+ paused: isPaused,
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Get metrics for all queues
185
+ */
186
+ export async function getAllQueueMetrics(): Promise<Record<QueueName, QueueMetrics>> {
187
+ const queueNames = [
188
+ QueueName.EXTRACTION,
189
+ QueueName.CHUNKING,
190
+ QueueName.EMBEDDING,
191
+ QueueName.INDEXING,
192
+ QueueName.DEAD_LETTER,
193
+ ]
194
+
195
+ const metricsPromises = queueNames.map(async (name) => ({
196
+ name,
197
+ metrics: await getQueueMetrics(name),
198
+ }))
199
+
200
+ const results = await Promise.all(metricsPromises)
201
+
202
+ return results.reduce(
203
+ (acc, { name, metrics }) => {
204
+ acc[name] = metrics
205
+ return acc
206
+ },
207
+ {} as Record<QueueName, QueueMetrics>
208
+ )
209
+ }
210
+
211
+ /**
212
+ * Move failed job to dead letter queue
213
+ */
214
+ export async function moveToDeadLetterQueue(queueName: QueueName, jobId: string, reason: string): Promise<string> {
215
+ const sourceQueue = getQueue(queueName)
216
+ const job = await sourceQueue.getJob(jobId)
217
+
218
+ if (!job) {
219
+ throw new NotFoundError('Job', jobId, ErrorCode.NOT_FOUND)
220
+ }
221
+
222
+ // Add to dead letter queue with original data plus metadata
223
+ const dlqJob = await deadLetterQueue.add(
224
+ 'failed-job',
225
+ {
226
+ originalQueue: queueName,
227
+ originalJobId: jobId,
228
+ originalData: job.data,
229
+ failureReason: reason,
230
+ attemptsMade: job.attemptsMade,
231
+ timestamp: new Date().toISOString(),
232
+ },
233
+ {
234
+ priority: 1, // Low priority for manual review
235
+ }
236
+ )
237
+
238
+ // Remove from original queue
239
+ await job.remove()
240
+ logger.info('Moved job to dead letter queue', { jobId, queueName, reason })
241
+
242
+ return dlqJob.id!
243
+ }
244
+
245
+ /**
246
+ * Update job progress
247
+ */
248
+ export async function updateJobProgress(queueName: QueueName, jobId: string, progress: JobProgress): Promise<void> {
249
+ const queue = getQueue(queueName)
250
+ const job = await queue.getJob(jobId)
251
+
252
+ if (!job) {
253
+ throw new NotFoundError('Job', jobId, ErrorCode.NOT_FOUND)
254
+ }
255
+
256
+ await job.updateProgress(progress)
257
+ }
258
+
259
+ /**
260
+ * Clean up old jobs across all queues
261
+ */
262
+ export async function cleanupQueues(gracePeriodMs = 24 * 60 * 60 * 1000): Promise<void> {
263
+ const queues = [extractionQueue, chunkingQueue, embeddingQueue, indexingQueue]
264
+
265
+ for (const queue of queues) {
266
+ await queue.clean(gracePeriodMs, 1000, 'completed')
267
+ await queue.clean(gracePeriodMs * 7, 1000, 'failed') // Keep failed jobs longer
268
+ }
269
+
270
+ logger.info('Queue cleanup completed', { gracePeriodMs })
271
+ }
272
+
273
+ /**
274
+ * Gracefully close all queues
275
+ */
276
+ export async function closeAllQueues(): Promise<void> {
277
+ const queues = [extractionQueue, chunkingQueue, embeddingQueue, indexingQueue, deadLetterQueue]
278
+
279
+ const events = [extractionEvents, chunkingEvents, embeddingEvents, indexingEvents]
280
+
281
+ await Promise.all([...queues.map((q) => q.close()), ...events.map((e) => e.close()), closeRedisConnection()])
282
+
283
+ logger.info('All queues closed gracefully')
284
+ }
285
+
286
+ // Export types and enums
287
+ export type { JobProgress }
288
+ export { JobPriority } from './config.js'
289
+ export { concurrencySettings } from './config.js'
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Supermemory Client
3
+ * Main entry point for the Supermemory SDK
4
+ */
5
+
6
+ import { HTTPClient, APIPromise, toFile } from './http.js'
7
+ import { Search, Documents, Memories, Connections, Settings } from './resources/index.js'
8
+ import type { ClientOptions, RequestOptions, AddParams, AddResponse, ProfileParams, ProfileResponse } from './types.js'
9
+
10
+ /**
11
+ * API Client for interfacing with the Supermemory API
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import Supermemory from 'supermemory';
16
+ *
17
+ * const client = new Supermemory({
18
+ * apiKey: process.env.SUPERMEMORY_API_KEY,
19
+ * });
20
+ *
21
+ * const response = await client.add({ content: 'Hello, world!' });
22
+ * console.log(response.id);
23
+ * ```
24
+ */
25
+ export class Supermemory {
26
+ private _client: HTTPClient
27
+ private _options: ClientOptions
28
+
29
+ // Resource instances
30
+ readonly search: Search
31
+ readonly documents: Documents
32
+ readonly memories: Memories
33
+ readonly connections: Connections
34
+ readonly settings: Settings
35
+
36
+ /**
37
+ * Create a new Supermemory client
38
+ *
39
+ * @param options - Client configuration options
40
+ */
41
+ constructor(options: ClientOptions = {}) {
42
+ this._options = options
43
+ this._client = new HTTPClient(options)
44
+
45
+ // Initialize resources
46
+ this.search = new Search(this._client)
47
+ this.documents = new Documents(this._client)
48
+ this.memories = new Memories(this._client)
49
+ this.connections = new Connections(this._client)
50
+ this.settings = new Settings(this._client)
51
+ }
52
+
53
+ // ============================================================================
54
+ // Top-Level Methods
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Add content to Supermemory
59
+ *
60
+ * This is the primary method for ingesting content. The content can be:
61
+ * - A URL to a website, PDF, image, or video
62
+ * - Plain text content
63
+ * - Markdown content
64
+ *
65
+ * @param body - Content and metadata to add
66
+ * @param options - Request options
67
+ * @returns Document ID and status
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // Add a URL
72
+ * const response = await client.add({
73
+ * content: 'https://example.com/article',
74
+ * containerTag: 'my-project',
75
+ * });
76
+ *
77
+ * // Add text content
78
+ * const response = await client.add({
79
+ * content: 'This is important information to remember.',
80
+ * metadata: { source: 'notes', priority: 'high' },
81
+ * });
82
+ * ```
83
+ */
84
+ add(body: AddParams, options?: RequestOptions): APIPromise<AddResponse> {
85
+ return this._client.post<AddResponse>('/v3/add', {
86
+ body,
87
+ requestOptions: options,
88
+ })
89
+ }
90
+
91
+ /**
92
+ * Get user profile with optional search context
93
+ *
94
+ * Returns both dynamic (recent) and static (long-term) memory profile
95
+ * for a given container tag. Optionally includes search results.
96
+ *
97
+ * @param body - Profile parameters including container tag
98
+ * @param options - Request options
99
+ * @returns Profile data and optional search results
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const profile = await client.profile({
104
+ * containerTag: 'user-123',
105
+ * q: 'preferences', // Optional search query
106
+ * });
107
+ *
108
+ * console.log('Dynamic memories:', profile.profile.dynamic);
109
+ * console.log('Static memories:', profile.profile.static);
110
+ * ```
111
+ */
112
+ profile(body: ProfileParams, options?: RequestOptions): APIPromise<ProfileResponse> {
113
+ return this._client.post<ProfileResponse>('/v3/profile', {
114
+ body,
115
+ requestOptions: options,
116
+ })
117
+ }
118
+
119
+ // ============================================================================
120
+ // Low-Level HTTP Methods
121
+ // ============================================================================
122
+
123
+ /**
124
+ * Make a raw GET request
125
+ *
126
+ * @param path - API path
127
+ * @param options - Request options
128
+ */
129
+ get<T>(path: string, options?: { query?: Record<string, unknown>; requestOptions?: RequestOptions }): APIPromise<T> {
130
+ return this._client.get<T>(path, options)
131
+ }
132
+
133
+ /**
134
+ * Make a raw POST request
135
+ *
136
+ * @param path - API path
137
+ * @param options - Request options
138
+ */
139
+ post<T>(
140
+ path: string,
141
+ options?: {
142
+ body?: unknown
143
+ query?: Record<string, unknown>
144
+ requestOptions?: RequestOptions
145
+ }
146
+ ): APIPromise<T> {
147
+ return this._client.post<T>(path, options)
148
+ }
149
+
150
+ /**
151
+ * Make a raw PUT request
152
+ *
153
+ * @param path - API path
154
+ * @param options - Request options
155
+ */
156
+ put<T>(
157
+ path: string,
158
+ options?: {
159
+ body?: unknown
160
+ query?: Record<string, unknown>
161
+ requestOptions?: RequestOptions
162
+ }
163
+ ): APIPromise<T> {
164
+ return this._client.put<T>(path, options)
165
+ }
166
+
167
+ /**
168
+ * Make a raw PATCH request
169
+ *
170
+ * @param path - API path
171
+ * @param options - Request options
172
+ */
173
+ patch<T>(
174
+ path: string,
175
+ options?: {
176
+ body?: unknown
177
+ query?: Record<string, unknown>
178
+ requestOptions?: RequestOptions
179
+ }
180
+ ): APIPromise<T> {
181
+ return this._client.patch<T>(path, options)
182
+ }
183
+
184
+ /**
185
+ * Make a raw DELETE request
186
+ *
187
+ * @param path - API path
188
+ * @param options - Request options
189
+ */
190
+ delete<T>(
191
+ path: string,
192
+ options?: {
193
+ body?: unknown
194
+ query?: Record<string, unknown>
195
+ requestOptions?: RequestOptions
196
+ }
197
+ ): APIPromise<T> {
198
+ return this._client.delete<T>(path, options)
199
+ }
200
+
201
+ // ============================================================================
202
+ // Configuration Methods
203
+ // ============================================================================
204
+
205
+ /**
206
+ * Create a new client with modified options
207
+ *
208
+ * @param options - Options to override
209
+ * @returns New client instance with merged options
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * const clientWithTimeout = client.withOptions({ timeout: 30000 });
214
+ * ```
215
+ */
216
+ withOptions(options: Partial<ClientOptions>): Supermemory {
217
+ return new Supermemory({
218
+ ...this._options,
219
+ ...options,
220
+ })
221
+ }
222
+ }
223
+
224
+ // Re-export for convenience
225
+ export { toFile }