@eldrin-project/eldrin-app-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,485 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Core types for @eldrin-project/eldrin-app-core
6
+ */
7
+ /**
8
+ * Migration file representation
9
+ */
10
+ interface MigrationFile {
11
+ /** Full filename including timestamp and description (e.g., "20250115120000-create-invoices.sql") */
12
+ name: string;
13
+ /** Raw SQL content of the migration file */
14
+ content: string;
15
+ }
16
+ /**
17
+ * Record of an executed migration stored in _eldrin_migrations table
18
+ */
19
+ interface MigrationRecord {
20
+ id: number;
21
+ filename: string;
22
+ checksum: string;
23
+ executed_at: number;
24
+ execution_time_ms: number | null;
25
+ }
26
+ /**
27
+ * Result of running migrations
28
+ */
29
+ interface MigrationResult {
30
+ /** Whether all migrations succeeded */
31
+ success: boolean;
32
+ /** Number of migrations that were executed */
33
+ executed: number;
34
+ /** Details about executed migrations */
35
+ migrations: Array<{
36
+ name: string;
37
+ executionTimeMs: number;
38
+ }>;
39
+ /** Error if migration failed */
40
+ error?: {
41
+ migration: string;
42
+ message: string;
43
+ sql?: string;
44
+ };
45
+ }
46
+ /**
47
+ * Migration runner options
48
+ */
49
+ interface MigrationOptions {
50
+ /** Directory containing migration files (for CLI/build-time) */
51
+ migrationsDir?: string;
52
+ /** Pre-loaded migration files (for Workers runtime) */
53
+ migrations?: MigrationFile[];
54
+ /** Skip checksum verification (development only) */
55
+ skipChecksumVerification?: boolean;
56
+ /** Callback for logging */
57
+ onLog?: (message: string, level: 'info' | 'warn' | 'error') => void;
58
+ }
59
+ /**
60
+ * App configuration from eldrin-app.manifest.json
61
+ */
62
+ interface AppManifest {
63
+ name: string;
64
+ version: string;
65
+ displayName?: string;
66
+ description?: string;
67
+ permissions?: string[];
68
+ hooks?: Record<string, string>;
69
+ events?: {
70
+ emits?: string[];
71
+ subscribes?: string[];
72
+ };
73
+ }
74
+ /**
75
+ * Options for createApp factory
76
+ */
77
+ interface CreateAppOptions {
78
+ /** Unique app identifier */
79
+ name: string;
80
+ /** Root React component */
81
+ root: React.ComponentType<unknown>;
82
+ /** App manifest (optional, can be loaded from file) */
83
+ manifest?: AppManifest;
84
+ /** Migration files (loaded at build time via Vite plugin) */
85
+ migrations?: MigrationFile[];
86
+ /** Called when migrations complete successfully */
87
+ onMigrationsComplete?: (result: MigrationResult) => void;
88
+ /** Called when migrations fail */
89
+ onMigrationError?: (error: Error) => void;
90
+ }
91
+ /**
92
+ * Database context passed to app components
93
+ */
94
+ interface DatabaseContext {
95
+ /** D1 database instance (null if app has no database) */
96
+ db: D1Database | null;
97
+ /** Whether migrations have completed */
98
+ migrationsComplete: boolean;
99
+ /** Migration result (if migrations were run) */
100
+ migrationResult?: MigrationResult;
101
+ }
102
+ /**
103
+ * Eldrin context provided to apps
104
+ */
105
+ interface EldrinContext {
106
+ /** App ID */
107
+ appId: string;
108
+ /** Database context */
109
+ database: DatabaseContext;
110
+ /** Environment variables */
111
+ env: Record<string, string>;
112
+ }
113
+
114
+ /**
115
+ * App factory for creating Eldrin apps
116
+ *
117
+ * Returns single-spa compatible lifecycle functions with:
118
+ * - Automatic migration execution on bootstrap
119
+ * - Database provider wrapping
120
+ * - Error boundary integration
121
+ */
122
+
123
+ /**
124
+ * Single-spa lifecycle props passed to lifecycle functions
125
+ */
126
+ interface LifecycleProps {
127
+ /** DOM element to mount the app into */
128
+ domElement?: HTMLElement;
129
+ /** App name */
130
+ name?: string;
131
+ /** D1 Database instance (provided by shell) */
132
+ db?: D1Database;
133
+ /** Custom props from shell */
134
+ customProps?: Record<string, unknown>;
135
+ }
136
+ /**
137
+ * Single-spa compatible lifecycle object
138
+ */
139
+ interface AppLifecycle {
140
+ bootstrap: (props: LifecycleProps) => Promise<void>;
141
+ mount: (props: LifecycleProps) => Promise<void>;
142
+ unmount: (props: LifecycleProps) => Promise<void>;
143
+ }
144
+ /**
145
+ * Create an Eldrin app with single-spa compatible lifecycle
146
+ *
147
+ * @param options - App configuration
148
+ * @returns Object with bootstrap, mount, and unmount lifecycle functions
149
+ *
150
+ * @example
151
+ * ```tsx
152
+ * // src/index.tsx
153
+ * import { createApp } from '@eldrin-project/eldrin-app-core';
154
+ * import App from './App';
155
+ *
156
+ * export const { bootstrap, mount, unmount } = createApp({
157
+ * name: 'invoicing',
158
+ * root: App,
159
+ * // Migrations loaded via Vite plugin
160
+ * });
161
+ * ```
162
+ */
163
+ declare function createApp(options: CreateAppOptions): AppLifecycle;
164
+ /**
165
+ * Type helper for migration files loaded via Vite plugin
166
+ *
167
+ * @example
168
+ * ```tsx
169
+ * // With Vite plugin
170
+ * import { createApp, type MigrationFiles } from '@eldrin-project/eldrin-app-core';
171
+ * import migrations from 'virtual:eldrin/migrations';
172
+ *
173
+ * export const { bootstrap, mount, unmount } = createApp({
174
+ * name: 'invoicing',
175
+ * root: App,
176
+ * migrations: migrations as MigrationFiles,
177
+ * });
178
+ * ```
179
+ */
180
+ type MigrationFiles = MigrationFile[];
181
+
182
+ /**
183
+ * Props for DatabaseProvider
184
+ */
185
+ interface DatabaseProviderProps {
186
+ /** D1 database instance (null if app has no database) */
187
+ db: D1Database | null;
188
+ /** Whether migrations have completed */
189
+ migrationsComplete: boolean;
190
+ /** Migration result */
191
+ migrationResult?: MigrationResult;
192
+ /** Child components */
193
+ children: ReactNode;
194
+ }
195
+ /**
196
+ * Provider component for database context
197
+ */
198
+ declare function DatabaseProvider({ db, migrationsComplete, migrationResult, children, }: DatabaseProviderProps): react_jsx_runtime.JSX.Element;
199
+ /**
200
+ * Hook to access the app's D1 database
201
+ *
202
+ * @returns D1Database instance or null if app has no database
203
+ * @throws Error if used outside of DatabaseProvider
204
+ *
205
+ * @example
206
+ * ```tsx
207
+ * function InvoiceList() {
208
+ * const db = useDatabase();
209
+ *
210
+ * if (!db) {
211
+ * return <div>No database configured</div>;
212
+ * }
213
+ *
214
+ * const { results } = await db.prepare(
215
+ * 'SELECT * FROM invoices ORDER BY created_at DESC'
216
+ * ).all();
217
+ *
218
+ * return <div>{results.map(invoice => ...)}</div>;
219
+ * }
220
+ * ```
221
+ */
222
+ declare function useDatabase(): D1Database | null;
223
+ /**
224
+ * Hook to access full database context including migration status
225
+ *
226
+ * @returns DatabaseContext object
227
+ * @throws Error if used outside of DatabaseProvider
228
+ */
229
+ declare function useDatabaseContext(): DatabaseContext;
230
+ /**
231
+ * Hook to check if migrations have completed
232
+ *
233
+ * Useful for showing loading states while migrations run
234
+ *
235
+ * @returns true if migrations are complete
236
+ */
237
+ declare function useMigrationsComplete(): boolean;
238
+
239
+ /**
240
+ * Migration runner for Eldrin apps
241
+ *
242
+ * Executes SQL migrations against a D1 database with:
243
+ * - Automatic tracking table creation
244
+ * - Checksum verification for integrity
245
+ * - Sequential execution (one migration at a time)
246
+ * - Transaction-based execution per migration
247
+ */
248
+
249
+ /**
250
+ * Run pending migrations against the database
251
+ *
252
+ * @param db - D1 Database instance
253
+ * @param options - Migration options
254
+ * @returns Result of migration execution
255
+ */
256
+ declare function runMigrations(db: D1Database, options?: MigrationOptions): Promise<MigrationResult>;
257
+ /**
258
+ * Get migration status (pending and executed migrations)
259
+ *
260
+ * @param db - D1 Database instance
261
+ * @param migrations - Available migration files
262
+ * @returns Status information
263
+ */
264
+ declare function getMigrationStatus(db: D1Database, migrations: MigrationFile[]): Promise<{
265
+ pending: string[];
266
+ executed: MigrationRecord[];
267
+ orphaned: string[];
268
+ checksumMismatches: string[];
269
+ }>;
270
+
271
+ /**
272
+ * Migration rollback functionality
273
+ *
274
+ * Rolls back migrations using .rollback.sql files in reverse order
275
+ */
276
+
277
+ /**
278
+ * Result of a rollback operation
279
+ */
280
+ interface RollbackResult {
281
+ success: boolean;
282
+ rolledBack: string[];
283
+ error?: {
284
+ migration: string;
285
+ message: string;
286
+ };
287
+ }
288
+ /**
289
+ * Rollback migrations to a specific target (or just the last one)
290
+ *
291
+ * @param db - D1 Database instance
292
+ * @param options - Rollback options
293
+ * @returns Result of rollback operation
294
+ */
295
+ declare function rollbackMigrations(db: D1Database, options: {
296
+ /** Rollback files (.rollback.sql) */
297
+ rollbackFiles: MigrationFile[];
298
+ /** Target migration to rollback TO (exclusive - this migration stays) */
299
+ targetMigration?: string;
300
+ /** Callback for logging */
301
+ onLog?: (message: string, level: 'info' | 'warn' | 'error') => void;
302
+ }): Promise<RollbackResult>;
303
+
304
+ /**
305
+ * Checksum utilities for migration integrity verification
306
+ *
307
+ * Uses SHA-256 for content hashing via Web Crypto API
308
+ * (compatible with Cloudflare Workers runtime)
309
+ */
310
+ /** Prefix for SHA-256 checksums in marketplace format */
311
+ declare const CHECKSUM_PREFIX = "sha256:";
312
+ /**
313
+ * Calculate SHA-256 checksum of content
314
+ *
315
+ * @param content - String content to hash
316
+ * @param options - Options for checksum calculation
317
+ * @param options.prefixed - If true, returns "sha256:..." format (default: false for backwards compatibility)
318
+ * @returns Hex-encoded SHA-256 hash, optionally with prefix
319
+ */
320
+ declare function calculateChecksum(content: string, options?: {
321
+ prefixed?: boolean;
322
+ }): Promise<string>;
323
+ /**
324
+ * Calculate SHA-256 checksum with sha256: prefix
325
+ *
326
+ * Convenience function for marketplace format
327
+ *
328
+ * @param content - String content to hash
329
+ * @returns Prefixed checksum (e.g., "sha256:abc123...")
330
+ */
331
+ declare function calculatePrefixedChecksum(content: string): Promise<string>;
332
+ /**
333
+ * Verify that content matches an expected checksum
334
+ *
335
+ * Handles both prefixed ("sha256:...") and unprefixed checksums
336
+ *
337
+ * @param content - Content to verify
338
+ * @param expectedChecksum - Expected SHA-256 hex string (with or without prefix)
339
+ * @returns true if checksums match
340
+ */
341
+ declare function verifyChecksum(content: string, expectedChecksum: string): Promise<boolean>;
342
+
343
+ /**
344
+ * SQL statement parser for migration files
345
+ *
346
+ * Parses SQL content into individual statements for execution via db.batch()
347
+ */
348
+ /**
349
+ * Parse SQL content into individual statements
350
+ *
351
+ * - Removes SQL comments (-- style)
352
+ * - Splits by semicolon
353
+ * - Handles multi-line statements
354
+ * - Preserves string literals containing semicolons
355
+ *
356
+ * @param sql - Raw SQL content
357
+ * @returns Array of individual SQL statements
358
+ */
359
+ declare function parseSQLStatements(sql: string): string[];
360
+ /**
361
+ * Validate that a filename follows the migration naming convention
362
+ *
363
+ * Expected format: TIMESTAMP-description.sql
364
+ * - TIMESTAMP: 14 digits (YYYYMMDDHHmmss)
365
+ * - description: kebab-case
366
+ *
367
+ * @param filename - Filename to validate
368
+ * @returns true if valid, false otherwise
369
+ */
370
+ declare function isValidMigrationFilename(filename: string): boolean;
371
+ /**
372
+ * Validate that a filename follows the rollback naming convention
373
+ *
374
+ * Expected format: TIMESTAMP-description.rollback.sql
375
+ *
376
+ * @param filename - Filename to validate
377
+ * @returns true if valid rollback file, false otherwise
378
+ */
379
+ declare function isValidRollbackFilename(filename: string): boolean;
380
+ /**
381
+ * Extract timestamp from migration filename
382
+ *
383
+ * @param filename - Migration filename
384
+ * @returns Timestamp string or null if invalid
385
+ */
386
+ declare function extractTimestamp(filename: string): string | null;
387
+ /**
388
+ * Get the rollback filename for a migration
389
+ *
390
+ * @param migrationFilename - Migration filename (e.g., "20250115120000-create-table.sql")
391
+ * @returns Rollback filename (e.g., "20250115120000-create-table.rollback.sql")
392
+ */
393
+ declare function getRollbackFilename(migrationFilename: string): string;
394
+
395
+ /**
396
+ * Marketplace utilities for migration packaging
397
+ *
398
+ * Provides tools to generate migration manifests for marketplace distribution.
399
+ * This is used at build time to prepare apps for submission.
400
+ */
401
+
402
+ /**
403
+ * Migration entry in the marketplace manifest
404
+ */
405
+ interface MigrationManifestEntry {
406
+ /** Migration ID (timestamp) */
407
+ id: string;
408
+ /** Full filename */
409
+ file: string;
410
+ /** SHA-256 checksum with prefix */
411
+ checksum: string;
412
+ }
413
+ /**
414
+ * Marketplace migration manifest format
415
+ */
416
+ interface MigrationManifest {
417
+ /** Logical database name (used for table prefixing) */
418
+ database: string;
419
+ /** List of migrations in order */
420
+ migrations: MigrationManifestEntry[];
421
+ }
422
+ /**
423
+ * Options for generating migration manifest
424
+ */
425
+ interface GenerateMigrationManifestOptions {
426
+ /** Directory containing migration SQL files */
427
+ migrationsDir: string;
428
+ /** Logical database name for the app */
429
+ database: string;
430
+ }
431
+ /**
432
+ * Result of migration manifest generation
433
+ */
434
+ interface GenerateMigrationManifestResult {
435
+ /** The generated manifest */
436
+ manifest: MigrationManifest;
437
+ /** Migration files with content (for copying to output) */
438
+ files: MigrationFile[];
439
+ }
440
+ /**
441
+ * Generate a marketplace migration manifest from migration files
442
+ *
443
+ * Reads migration files from the specified directory and generates
444
+ * a manifest with checksums suitable for marketplace distribution.
445
+ *
446
+ * @param options - Generation options
447
+ * @returns Manifest and file contents
448
+ *
449
+ * @example
450
+ * ```typescript
451
+ * import { generateMigrationManifest } from '@eldrin-project/eldrin-app-core';
452
+ *
453
+ * const result = await generateMigrationManifest({
454
+ * migrationsDir: './migrations',
455
+ * database: 'invoicing',
456
+ * });
457
+ *
458
+ * // Write manifest to output directory
459
+ * await writeFile(
460
+ * 'dist/migrations/index.json',
461
+ * JSON.stringify(result.manifest, null, 2)
462
+ * );
463
+ *
464
+ * // Copy migration files
465
+ * for (const file of result.files) {
466
+ * await writeFile(`dist/migrations/${file.name}`, file.content);
467
+ * }
468
+ * ```
469
+ */
470
+ declare function generateMigrationManifest(options: GenerateMigrationManifestOptions): Promise<GenerateMigrationManifestResult>;
471
+ /**
472
+ * Validate a migration manifest against actual files
473
+ *
474
+ * Useful for CI/CD validation in the marketplace-dist repository.
475
+ *
476
+ * @param manifest - The manifest to validate
477
+ * @param files - The actual migration files
478
+ * @returns Validation result with any errors
479
+ */
480
+ declare function validateMigrationManifest(manifest: MigrationManifest, files: MigrationFile[]): Promise<{
481
+ valid: boolean;
482
+ errors: string[];
483
+ }>;
484
+
485
+ export { type AppLifecycle, type AppManifest, CHECKSUM_PREFIX, type CreateAppOptions, type DatabaseContext, DatabaseProvider, type DatabaseProviderProps, type EldrinContext, type GenerateMigrationManifestOptions, type GenerateMigrationManifestResult, type LifecycleProps, type MigrationFile, type MigrationFiles, type MigrationManifest, type MigrationManifestEntry, type MigrationOptions, type MigrationRecord, type MigrationResult, type RollbackResult, calculateChecksum, calculatePrefixedChecksum, createApp, extractTimestamp, generateMigrationManifest, getMigrationStatus, getRollbackFilename, isValidMigrationFilename, isValidRollbackFilename, parseSQLStatements, rollbackMigrations, runMigrations, useDatabase, useDatabaseContext, useMigrationsComplete, validateMigrationManifest, verifyChecksum };