@maravilla-labs/platform 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.
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Key-Value namespace interface for storing and retrieving data.
3
+ * Similar to Cloudflare Workers KV API for familiar developer experience.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const platform = getPlatform();
8
+ * const userKv = platform.env.KV.users;
9
+ *
10
+ * // Store user data
11
+ * await userKv.put('user:123', { name: 'John', email: 'john@example.com' });
12
+ *
13
+ * // Retrieve user data
14
+ * const user = await userKv.get('user:123');
15
+ *
16
+ * // List all user keys
17
+ * const result = await userKv.list({ prefix: 'user:' });
18
+ * ```
19
+ */
20
+ interface KvNamespace {
21
+ /**
22
+ * Retrieve a value from the KV namespace.
23
+ *
24
+ * @param key - The key to retrieve
25
+ * @returns Promise that resolves to the stored value, or null if not found
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const user = await kv.get('user:123');
30
+ * if (user) {
31
+ * console.log(`Welcome back, ${user.name}!`);
32
+ * }
33
+ * ```
34
+ */
35
+ get(key: string): Promise<any>;
36
+ /**
37
+ * Store a value in the KV namespace.
38
+ *
39
+ * @param key - The key to store under
40
+ * @param value - The value to store (will be JSON serialized)
41
+ * @param options - Optional configuration
42
+ * @param options.expirationTtl - Time to live in seconds before the key expires
43
+ * @returns Promise that resolves when the operation completes
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * // Store permanent data
48
+ * await kv.put('config:theme', { dark: true });
49
+ *
50
+ * // Store temporary data (expires in 1 hour)
51
+ * await kv.put('session:abc123', sessionData, { expirationTtl: 3600 });
52
+ * ```
53
+ */
54
+ put(key: string, value: any, options?: {
55
+ expirationTtl?: number;
56
+ }): Promise<void>;
57
+ /**
58
+ * Delete a key from the KV namespace.
59
+ *
60
+ * @param key - The key to delete
61
+ * @returns Promise that resolves when the operation completes
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * await kv.delete('user:123');
66
+ * ```
67
+ */
68
+ delete(key: string): Promise<void>;
69
+ /**
70
+ * List keys in the KV namespace with optional filtering.
71
+ *
72
+ * @param options - Optional filtering and pagination options
73
+ * @param options.prefix - Only return keys that start with this prefix
74
+ * @param options.limit - Maximum number of keys to return (default varies by implementation)
75
+ * @param options.cursor - Pagination cursor from previous list call
76
+ * @returns Promise that resolves to a list result with keys and pagination info
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * // List all keys
81
+ * const all = await kv.list();
82
+ *
83
+ * // List user keys only
84
+ * const users = await kv.list({ prefix: 'user:' });
85
+ *
86
+ * // Paginated listing
87
+ * const page1 = await kv.list({ limit: 10 });
88
+ * if (!page1.list_complete) {
89
+ * const page2 = await kv.list({ limit: 10, cursor: page1.cursor });
90
+ * }
91
+ * ```
92
+ */
93
+ list(options?: {
94
+ prefix?: string;
95
+ limit?: number;
96
+ cursor?: string;
97
+ }): Promise<KvListResult>;
98
+ }
99
+ /**
100
+ * Result object returned by KV list operations.
101
+ * Contains the matching keys and pagination information.
102
+ */
103
+ interface KvListResult {
104
+ /** Array of matching keys with their metadata */
105
+ keys: Array<{
106
+ /** The key name */
107
+ name: string;
108
+ /** Unix timestamp when the key expires (if set) */
109
+ expiration?: number;
110
+ /** Custom metadata associated with the key */
111
+ metadata?: any;
112
+ }>;
113
+ /** Whether this result contains all matching keys (true) or more results are available (false) */
114
+ list_complete: boolean;
115
+ /** Pagination cursor to use for the next list call, if list_complete is false */
116
+ cursor?: string;
117
+ }
118
+ /**
119
+ * Database interface for document-based operations.
120
+ * Provides MongoDB-like API for storing and querying structured data.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const platform = getPlatform();
125
+ * const db = platform.env.DB;
126
+ *
127
+ * // Insert a new user
128
+ * const userId = await db.insertOne('users', {
129
+ * name: 'John Doe',
130
+ * email: 'john@example.com',
131
+ * createdAt: new Date()
132
+ * });
133
+ *
134
+ * // Find users by criteria
135
+ * const activeUsers = await db.find('users', { active: true });
136
+ *
137
+ * // Update a user
138
+ * await db.updateOne('users',
139
+ * { _id: userId },
140
+ * { $set: { lastLogin: new Date() } }
141
+ * );
142
+ * ```
143
+ */
144
+ interface Database {
145
+ /**
146
+ * Find multiple documents in a collection.
147
+ *
148
+ * @param collection - The collection name
149
+ * @param filter - Query filter object (MongoDB-style)
150
+ * @param options - Optional query options for pagination and sorting
151
+ * @returns Promise that resolves to an array of matching documents
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // Find all active users
156
+ * const users = await db.find('users', { active: true });
157
+ *
158
+ * // Find with pagination and sorting
159
+ * const recentPosts = await db.find('posts',
160
+ * { published: true },
161
+ * { limit: 10, sort: { createdAt: -1 } }
162
+ * );
163
+ * ```
164
+ */
165
+ find(collection: string, filter?: any, options?: DbFindOptions): Promise<any[]>;
166
+ /**
167
+ * Find a single document in a collection.
168
+ *
169
+ * @param collection - The collection name
170
+ * @param filter - Query filter object to match the document
171
+ * @returns Promise that resolves to the matching document, or null if not found
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const user = await db.findOne('users', { email: 'john@example.com' });
176
+ * if (user) {
177
+ * console.log(`Found user: ${user.name}`);
178
+ * }
179
+ * ```
180
+ */
181
+ findOne(collection: string, filter: any): Promise<any | null>;
182
+ /**
183
+ * Insert a single document into a collection.
184
+ *
185
+ * @param collection - The collection name
186
+ * @param document - The document to insert
187
+ * @returns Promise that resolves to the ID of the inserted document
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * const postId = await db.insertOne('posts', {
192
+ * title: 'Hello World',
193
+ * content: 'This is my first post',
194
+ * author: 'john@example.com',
195
+ * createdAt: new Date()
196
+ * });
197
+ * ```
198
+ */
199
+ insertOne(collection: string, document: any): Promise<string>;
200
+ /**
201
+ * Update a single document in a collection.
202
+ *
203
+ * @param collection - The collection name
204
+ * @param filter - Query filter to match the document to update
205
+ * @param update - Update operations (MongoDB-style with $set, $inc, etc.)
206
+ * @returns Promise that resolves to an object with the number of modified documents
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const result = await db.updateOne('users',
211
+ * { email: 'john@example.com' },
212
+ * { $set: { lastLogin: new Date() }, $inc: { loginCount: 1 } }
213
+ * );
214
+ * console.log(`Modified ${result.modified} documents`);
215
+ * ```
216
+ */
217
+ updateOne(collection: string, filter: any, update: any): Promise<{
218
+ modified: number;
219
+ }>;
220
+ /**
221
+ * Delete a single document from a collection.
222
+ *
223
+ * @param collection - The collection name
224
+ * @param filter - Query filter to match the document to delete
225
+ * @returns Promise that resolves to an object with the number of deleted documents
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * const result = await db.deleteOne('posts', { _id: postId });
230
+ * console.log(`Deleted ${result.deleted} documents`);
231
+ * ```
232
+ */
233
+ deleteOne(collection: string, filter: any): Promise<{
234
+ deleted: number;
235
+ }>;
236
+ /**
237
+ * Delete multiple documents from a collection.
238
+ *
239
+ * @param collection - The collection name
240
+ * @param filter - Query filter to match documents to delete
241
+ * @returns Promise that resolves to an object with the number of deleted documents
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * // Delete all inactive users
246
+ * const result = await db.deleteMany('users', { active: false });
247
+ * console.log(`Deleted ${result.deleted} inactive users`);
248
+ * ```
249
+ */
250
+ deleteMany(collection: string, filter: any): Promise<{
251
+ deleted: number;
252
+ }>;
253
+ }
254
+ /**
255
+ * Options for database find operations.
256
+ * Provides MongoDB-style query options for pagination, sorting, and limiting results.
257
+ */
258
+ interface DbFindOptions {
259
+ /** Maximum number of documents to return */
260
+ limit?: number;
261
+ /** Number of documents to skip (for pagination) */
262
+ skip?: number;
263
+ /** Sort order specification (MongoDB-style: { field: 1 } for ascending, { field: -1 } for descending) */
264
+ sort?: any;
265
+ }
266
+ /**
267
+ * Platform environment containing all available services.
268
+ * This is the main interface for accessing KV storage and database operations.
269
+ */
270
+ interface PlatformEnv {
271
+ /**
272
+ * Key-Value storage namespaces. Access different KV namespaces by property name.
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * const userCache = platform.env.KV.users;
277
+ * const sessionStore = platform.env.KV.sessions;
278
+ * ```
279
+ */
280
+ KV: Record<string, KvNamespace>;
281
+ /** Database interface for document operations */
282
+ DB: Database;
283
+ }
284
+ /**
285
+ * Main platform interface providing access to all Maravilla runtime services.
286
+ * This is the primary interface developers will interact with.
287
+ *
288
+ * @example
289
+ * ```typescript
290
+ * import { getPlatform } from '@maravilla/platform';
291
+ *
292
+ * const platform = getPlatform();
293
+ *
294
+ * // Use KV storage
295
+ * await platform.env.KV.cache.put('key', 'value');
296
+ *
297
+ * // Use database
298
+ * await platform.env.DB.insertOne('users', { name: 'John' });
299
+ * ```
300
+ */
301
+ interface Platform {
302
+ /** Environment containing all available platform services */
303
+ env: PlatformEnv;
304
+ }
305
+
306
+ /**
307
+ * @fileoverview Maravilla Platform SDK
308
+ *
309
+ * This package provides the main interface for accessing Maravilla runtime services
310
+ * including Key-Value storage and Database operations. It automatically detects the
311
+ * runtime environment and provides the appropriate implementation.
312
+ *
313
+ * ## Environment Detection
314
+ *
315
+ * The SDK automatically detects and adapts to different environments:
316
+ * - **Production**: Uses native Maravilla runtime APIs
317
+ * - **Development**: Connects to development server via HTTP
318
+ * - **Testing**: Can be mocked or use development server
319
+ *
320
+ * ## Key Features
321
+ *
322
+ * - **KV Storage**: Cloudflare Workers KV-compatible API for key-value operations
323
+ * - **Database**: MongoDB-style document database operations
324
+ * - **Multi-tenancy**: Built-in tenant isolation and support
325
+ * - **TypeScript**: Fully typed APIs with comprehensive JSDoc comments
326
+ * - **Development-friendly**: Seamless development server integration
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * import { getPlatform } from '@maravilla/platform';
331
+ *
332
+ * const platform = getPlatform();
333
+ *
334
+ * // KV operations
335
+ * await platform.env.KV.cache.put('user:123', { name: 'John' });
336
+ * const user = await platform.env.KV.cache.get('user:123');
337
+ *
338
+ * // Database operations
339
+ * const userId = await platform.env.DB.insertOne('users', {
340
+ * name: 'John Doe',
341
+ * email: 'john@example.com'
342
+ * });
343
+ *
344
+ * const users = await platform.env.DB.find('users', { active: true });
345
+ * ```
346
+ *
347
+ * @author Maravilla Team
348
+ * @since 1.0.0
349
+ */
350
+
351
+ /**
352
+ * Global platform instance injected by Maravilla runtime or development tools.
353
+ * This should not be accessed directly - use getPlatform() instead.
354
+ *
355
+ * @internal
356
+ */
357
+ declare global {
358
+ var __maravilla_platform: Platform | undefined;
359
+ var platform: Platform | undefined;
360
+ }
361
+ /**
362
+ * Get the platform instance. This will:
363
+ * 1. Check if running in Maravilla runtime (global.platform exists)
364
+ * 2. Check if a platform was injected via vite plugin or hooks
365
+ * 3. Fall back to remote client for development
366
+ *
367
+ * The platform provides access to KV storage and database operations,
368
+ * with automatic environment detection for seamless development and production.
369
+ *
370
+ * @param options - Optional configuration for development mode
371
+ * @param options.devServerUrl - URL of the development server (defaults to MARAVILLA_DEV_SERVER env var or http://localhost:3001)
372
+ * @param options.tenant - Tenant identifier for multi-tenancy (defaults to MARAVILLA_TENANT env var or 'dev-tenant')
373
+ * @returns Platform instance with access to KV and database services
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * import { getPlatform } from '@maravilla/platform';
378
+ *
379
+ * // Basic usage (auto-detects environment)
380
+ * const platform = getPlatform();
381
+ *
382
+ * // Development with custom server
383
+ * const platform = getPlatform({
384
+ * devServerUrl: 'http://localhost:3001',
385
+ * tenant: 'my-app'
386
+ * });
387
+ *
388
+ * // Use KV storage
389
+ * await platform.env.KV.cache.put('user:123', { name: 'John' });
390
+ * const user = await platform.env.KV.cache.get('user:123');
391
+ *
392
+ * // Use database
393
+ * const userId = await platform.env.DB.insertOne('users', {
394
+ * name: 'John Doe',
395
+ * email: 'john@example.com'
396
+ * });
397
+ * ```
398
+ */
399
+ declare function getPlatform(options?: {
400
+ devServerUrl?: string;
401
+ tenant?: string;
402
+ }): Platform;
403
+ /**
404
+ * Clear the cached platform instance (useful for testing).
405
+ *
406
+ * This function resets the internal platform cache and clears any globally
407
+ * injected platform instances. Subsequent calls to getPlatform() will
408
+ * re-initialize the platform based on the current environment.
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * import { clearPlatformCache, getPlatform } from '@maravilla/platform';
413
+ *
414
+ * // In tests, clear cache between test cases
415
+ * afterEach(() => {
416
+ * clearPlatformCache();
417
+ * });
418
+ *
419
+ * // Or when switching between different environments
420
+ * clearPlatformCache();
421
+ * const newPlatform = getPlatform({ devServerUrl: 'http://localhost:3002' });
422
+ * ```
423
+ */
424
+ declare function clearPlatformCache(): void;
425
+
426
+ export { type Database, type DbFindOptions, type KvListResult, type KvNamespace, type Platform, type PlatformEnv, clearPlatformCache, getPlatform };
package/dist/index.js ADDED
@@ -0,0 +1,178 @@
1
+ // src/remote-client.ts
2
+ var RemoteKvNamespace = class {
3
+ constructor(baseUrl, namespace, headers) {
4
+ this.baseUrl = baseUrl;
5
+ this.namespace = namespace;
6
+ this.headers = headers;
7
+ }
8
+ /**
9
+ * Internal method for making HTTP requests to the dev server.
10
+ * Handles error responses and authentication headers.
11
+ *
12
+ * @internal
13
+ */
14
+ async fetch(url, options = {}) {
15
+ const response = await fetch(url, {
16
+ ...options,
17
+ headers: { ...this.headers, ...options.headers }
18
+ });
19
+ if (!response.ok && response.status !== 404) {
20
+ const error = await response.text();
21
+ throw new Error(`Platform API error: ${response.status} - ${error}`);
22
+ }
23
+ return response;
24
+ }
25
+ async get(key) {
26
+ const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`);
27
+ if (response.status === 404) return null;
28
+ return response.json();
29
+ }
30
+ async put(key, value, options) {
31
+ const headers = {};
32
+ if (options?.expirationTtl) {
33
+ headers["X-TTL"] = options.expirationTtl.toString();
34
+ }
35
+ await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {
36
+ method: "PUT",
37
+ headers,
38
+ body: JSON.stringify(value)
39
+ });
40
+ }
41
+ async delete(key) {
42
+ await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {
43
+ method: "DELETE"
44
+ });
45
+ }
46
+ async list(options) {
47
+ const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}`, {
48
+ method: "POST",
49
+ body: JSON.stringify(options || {})
50
+ });
51
+ const data = await response.json();
52
+ return {
53
+ keys: data.result || [],
54
+ list_complete: !data.result_info?.cursor,
55
+ cursor: data.result_info?.cursor
56
+ };
57
+ }
58
+ };
59
+ var RemoteDatabase = class {
60
+ constructor(baseUrl, headers) {
61
+ this.baseUrl = baseUrl;
62
+ this.headers = headers;
63
+ }
64
+ /**
65
+ * Internal method for making HTTP requests to the dev server.
66
+ * Handles error responses and authentication headers.
67
+ *
68
+ * @internal
69
+ */
70
+ async fetch(url, options = {}) {
71
+ const response = await fetch(url, {
72
+ ...options,
73
+ headers: { ...this.headers, ...options.headers }
74
+ });
75
+ if (!response.ok && response.status !== 404) {
76
+ const error = await response.text();
77
+ throw new Error(`Platform API error: ${response.status} - ${error}`);
78
+ }
79
+ return response;
80
+ }
81
+ async find(collection, filter = {}, options = {}) {
82
+ const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {
83
+ method: "POST",
84
+ body: JSON.stringify({ filter, options })
85
+ });
86
+ return response.json();
87
+ }
88
+ async findOne(collection, filter) {
89
+ const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/findOne`, {
90
+ method: "POST",
91
+ body: JSON.stringify(filter)
92
+ });
93
+ if (response.status === 404) return null;
94
+ return response.json();
95
+ }
96
+ async insertOne(collection, document) {
97
+ const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {
98
+ method: "PUT",
99
+ body: JSON.stringify(document)
100
+ });
101
+ const result = await response.json();
102
+ return result.id;
103
+ }
104
+ async updateOne(collection, filter, update) {
105
+ const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/update`, {
106
+ method: "POST",
107
+ body: JSON.stringify({ filter, update })
108
+ });
109
+ return response.json();
110
+ }
111
+ async deleteOne(collection, filter) {
112
+ const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/delete`, {
113
+ method: "DELETE",
114
+ body: JSON.stringify(filter)
115
+ });
116
+ return response.json();
117
+ }
118
+ async deleteMany(collection, filter) {
119
+ return this.deleteOne(collection, filter);
120
+ }
121
+ };
122
+ function createRemoteClient(baseUrl, tenant) {
123
+ const headers = {
124
+ "Content-Type": "application/json",
125
+ "X-Tenant-Id": tenant
126
+ };
127
+ const kvProxy = new Proxy({}, {
128
+ get(_, namespace) {
129
+ return new RemoteKvNamespace(baseUrl, namespace, headers);
130
+ }
131
+ });
132
+ const db = new RemoteDatabase(baseUrl, headers);
133
+ return {
134
+ env: {
135
+ KV: kvProxy,
136
+ DB: db
137
+ }
138
+ };
139
+ }
140
+
141
+ // src/index.ts
142
+ var cachedPlatform = null;
143
+ function getPlatform(options) {
144
+ if (cachedPlatform) {
145
+ return cachedPlatform;
146
+ }
147
+ if (typeof globalThis !== "undefined") {
148
+ if (globalThis.platform) {
149
+ console.log("[platform] Using native Maravilla platform");
150
+ cachedPlatform = globalThis.platform;
151
+ return cachedPlatform;
152
+ }
153
+ if (globalThis.__maravilla_platform) {
154
+ console.log("[platform] Using injected platform");
155
+ cachedPlatform = globalThis.__maravilla_platform;
156
+ return cachedPlatform;
157
+ }
158
+ }
159
+ const devServerUrl = options?.devServerUrl || process.env.MARAVILLA_DEV_SERVER || "http://localhost:3001";
160
+ const tenant = options?.tenant || process.env.MARAVILLA_TENANT || "dev-tenant";
161
+ console.log(`[platform] Creating remote client for ${devServerUrl}`);
162
+ cachedPlatform = createRemoteClient(devServerUrl, tenant);
163
+ if (typeof globalThis !== "undefined") {
164
+ globalThis.__maravilla_platform = cachedPlatform;
165
+ }
166
+ return cachedPlatform;
167
+ }
168
+ function clearPlatformCache() {
169
+ cachedPlatform = null;
170
+ if (typeof globalThis !== "undefined") {
171
+ globalThis.__maravilla_platform = void 0;
172
+ }
173
+ }
174
+ export {
175
+ clearPlatformCache,
176
+ getPlatform
177
+ };
178
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/remote-client.ts","../src/index.ts"],"sourcesContent":["import type { KvNamespace, KvListResult, Database, DbFindOptions } from './types.js';\n\n/**\n * Remote KV namespace implementation that communicates with a development server.\n * This class provides the KV interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteKvNamespace implements KvNamespace {\n constructor(\n private baseUrl: string,\n private namespace: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async get(key: string): Promise<any> {\n const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`);\n if (response.status === 404) return null;\n return response.json();\n }\n\n async put(key: string, value: any, options?: { expirationTtl?: number }): Promise<void> {\n const headers: Record<string, string> = {};\n if (options?.expirationTtl) {\n headers['X-TTL'] = options.expirationTtl.toString();\n }\n\n await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(value),\n });\n }\n\n async delete(key: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {\n method: 'DELETE',\n });\n }\n\n async list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<KvListResult> {\n const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}`, {\n method: 'POST',\n body: JSON.stringify(options || {}),\n });\n const data = await response.json() as any;\n return {\n keys: data.result || [],\n list_complete: !data.result_info?.cursor,\n cursor: data.result_info?.cursor,\n };\n }\n}\n\n/**\n * Remote Database implementation that communicates with a development server.\n * This class provides the Database interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteDatabase implements Database {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async find(collection: string, filter: any = {}, options: DbFindOptions = {}): Promise<any[]> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {\n method: 'POST',\n body: JSON.stringify({ filter, options }),\n });\n return response.json() as Promise<any[]>;\n }\n\n async findOne(collection: string, filter: any): Promise<any | null> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/findOne`, {\n method: 'POST',\n body: JSON.stringify(filter),\n });\n if (response.status === 404) return null;\n return response.json();\n }\n\n async insertOne(collection: string, document: any): Promise<string> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {\n method: 'PUT',\n body: JSON.stringify(document),\n });\n const result = await response.json() as any;\n return result.id;\n }\n\n async updateOne(collection: string, filter: any, update: any): Promise<{ modified: number }> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/update`, {\n method: 'POST',\n body: JSON.stringify({ filter, update }),\n });\n return response.json() as Promise<{ modified: number }>;\n }\n\n async deleteOne(collection: string, filter: any): Promise<{ deleted: number }> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/delete`, {\n method: 'DELETE',\n body: JSON.stringify(filter),\n });\n return response.json() as Promise<{ deleted: number }>;\n }\n\n async deleteMany(collection: string, filter: any): Promise<{ deleted: number }> {\n // For now, deleteMany is the same as deleteOne in the remote client\n // This would need to be implemented properly in the dev server\n return this.deleteOne(collection, filter);\n }\n}\n\n/**\n * Create a remote platform client for development environments.\n * \n * This function creates a platform instance that communicates with a development\n * server over HTTP. It's used internally by getPlatform() when running in development\n * mode and no native platform is available.\n * \n * @param baseUrl - The base URL of the development server\n * @param tenant - The tenant identifier for multi-tenancy support\n * @returns A platform instance that proxies requests to the development server\n * \n * @internal\n * \n * @example\n * ```typescript\n * // This is typically called internally by getPlatform()\n * const platform = createRemoteClient('http://localhost:3001', 'dev-tenant');\n * \n * // The returned platform works the same as the native one\n * await platform.env.KV.cache.put('key', 'value');\n * await platform.env.DB.insertOne('users', { name: 'John' });\n * ```\n */\nexport function createRemoteClient(baseUrl: string, tenant: string) {\n const headers = {\n 'Content-Type': 'application/json',\n 'X-Tenant-Id': tenant,\n };\n\n // Create KV namespaces proxy that dynamically creates namespace instances\n const kvProxy = new Proxy({} as Record<string, KvNamespace>, {\n get(_, namespace: string) {\n return new RemoteKvNamespace(baseUrl, namespace, headers);\n }\n });\n\n const db = new RemoteDatabase(baseUrl, headers);\n\n return {\n env: {\n KV: kvProxy,\n DB: db,\n }\n };\n}","/**\n * @fileoverview Maravilla Platform SDK\n * \n * This package provides the main interface for accessing Maravilla runtime services\n * including Key-Value storage and Database operations. It automatically detects the\n * runtime environment and provides the appropriate implementation.\n * \n * ## Environment Detection\n * \n * The SDK automatically detects and adapts to different environments:\n * - **Production**: Uses native Maravilla runtime APIs\n * - **Development**: Connects to development server via HTTP\n * - **Testing**: Can be mocked or use development server\n * \n * ## Key Features\n * \n * - **KV Storage**: Cloudflare Workers KV-compatible API for key-value operations\n * - **Database**: MongoDB-style document database operations\n * - **Multi-tenancy**: Built-in tenant isolation and support\n * - **TypeScript**: Fully typed APIs with comprehensive JSDoc comments\n * - **Development-friendly**: Seamless development server integration\n * \n * @example\n * ```typescript\n * import { getPlatform } from '@maravilla/platform';\n * \n * const platform = getPlatform();\n * \n * // KV operations\n * await platform.env.KV.cache.put('user:123', { name: 'John' });\n * const user = await platform.env.KV.cache.get('user:123');\n * \n * // Database operations\n * const userId = await platform.env.DB.insertOne('users', {\n * name: 'John Doe',\n * email: 'john@example.com'\n * });\n * \n * const users = await platform.env.DB.find('users', { active: true });\n * ```\n * \n * @author Maravilla Team\n * @since 1.0.0\n */\n\nimport type { Platform } from './types.js';\nimport { createRemoteClient } from './remote-client.js';\n\nexport * from './types.js';\n\n/**\n * Global platform instance injected by Maravilla runtime or development tools.\n * This should not be accessed directly - use getPlatform() instead.\n * \n * @internal\n */\n\ndeclare global {\n var __maravilla_platform: Platform | undefined;\n var platform: Platform | undefined;\n}\n\nlet cachedPlatform: Platform | null = null;\n\n/**\n * Get the platform instance. This will:\n * 1. Check if running in Maravilla runtime (global.platform exists)\n * 2. Check if a platform was injected via vite plugin or hooks\n * 3. Fall back to remote client for development\n * \n * The platform provides access to KV storage and database operations,\n * with automatic environment detection for seamless development and production.\n * \n * @param options - Optional configuration for development mode\n * @param options.devServerUrl - URL of the development server (defaults to MARAVILLA_DEV_SERVER env var or http://localhost:3001)\n * @param options.tenant - Tenant identifier for multi-tenancy (defaults to MARAVILLA_TENANT env var or 'dev-tenant')\n * @returns Platform instance with access to KV and database services\n * \n * @example\n * ```typescript\n * import { getPlatform } from '@maravilla/platform';\n * \n * // Basic usage (auto-detects environment)\n * const platform = getPlatform();\n * \n * // Development with custom server\n * const platform = getPlatform({\n * devServerUrl: 'http://localhost:3001',\n * tenant: 'my-app'\n * });\n * \n * // Use KV storage\n * await platform.env.KV.cache.put('user:123', { name: 'John' });\n * const user = await platform.env.KV.cache.get('user:123');\n * \n * // Use database\n * const userId = await platform.env.DB.insertOne('users', {\n * name: 'John Doe',\n * email: 'john@example.com'\n * });\n * ```\n */\nexport function getPlatform(options?: {\n devServerUrl?: string;\n tenant?: string;\n}): Platform {\n // Return cached if available\n if (cachedPlatform) {\n return cachedPlatform;\n }\n\n // 1. Check if we're in the real Maravilla runtime\n if (typeof globalThis !== 'undefined') {\n // Check for native platform (in production runtime)\n if (globalThis.platform) {\n console.log('[platform] Using native Maravilla platform');\n cachedPlatform = globalThis.platform;\n return cachedPlatform;\n }\n\n // Check for injected platform (from vite plugin or hooks)\n if (globalThis.__maravilla_platform) {\n console.log('[platform] Using injected platform');\n cachedPlatform = globalThis.__maravilla_platform;\n return cachedPlatform;\n }\n }\n\n // 2. Fall back to remote client for development\n const devServerUrl = options?.devServerUrl || process.env.MARAVILLA_DEV_SERVER || 'http://localhost:3001';\n const tenant = options?.tenant || process.env.MARAVILLA_TENANT || 'dev-tenant';\n \n console.log(`[platform] Creating remote client for ${devServerUrl}`);\n cachedPlatform = createRemoteClient(devServerUrl, tenant);\n \n // Cache it globally for other modules\n if (typeof globalThis !== 'undefined') {\n globalThis.__maravilla_platform = cachedPlatform;\n }\n \n return cachedPlatform;\n}\n\n/**\n * Clear the cached platform instance (useful for testing).\n * \n * This function resets the internal platform cache and clears any globally\n * injected platform instances. Subsequent calls to getPlatform() will\n * re-initialize the platform based on the current environment.\n * \n * @example\n * ```typescript\n * import { clearPlatformCache, getPlatform } from '@maravilla/platform';\n * \n * // In tests, clear cache between test cases\n * afterEach(() => {\n * clearPlatformCache();\n * });\n * \n * // Or when switching between different environments\n * clearPlatformCache();\n * const newPlatform = getPlatform({ devServerUrl: 'http://localhost:3002' });\n * ```\n */\nexport function clearPlatformCache(): void {\n cachedPlatform = null;\n if (typeof globalThis !== 'undefined') {\n globalThis.__maravilla_platform = undefined;\n }\n}"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@maravilla-labs/platform",
3
+ "version": "0.1.0",
4
+ "description": "Universal platform client for Maravilla runtime",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "typecheck": "tsc --noEmit"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^22.10.6",
22
+ "tsup": "^8.5.0",
23
+ "typescript": "^5.9.2"
24
+ },
25
+ "dependencies": {},
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }
File without changes