@eldrin-project/eldrin-app-core 0.0.4 → 0.0.5
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/README.md +321 -131
- package/dist/index.cjs +0 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -91
- package/dist/index.d.ts +1 -91
- package/dist/index.js +1 -74
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +110 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +104 -0
- package/dist/node.d.ts +104 -0
- package/dist/node.js +107 -0
- package/dist/node.js.map +1 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -2,17 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@eldrin-project/eldrin-app-core)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://www.typescriptlang.org/)
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
Framework-agnostic core library for building Eldrin marketplace apps on Cloudflare Workers with D1 databases.
|
|
8
7
|
|
|
9
|
-
##
|
|
8
|
+
## Overview
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
This package is the **foundation layer** of the Eldrin ecosystem:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
14
|
+
│ Your App │
|
|
15
|
+
├─────────────────────────────────────────────────────────────┤
|
|
16
|
+
│ Framework Adapter (choose one) │
|
|
17
|
+
│ • @eldrin-project/eldrin-app-angular │
|
|
18
|
+
│ • @eldrin-project/eldrin-app-react │
|
|
19
|
+
│ • @eldrin-project/eldrin-app-vue │
|
|
20
|
+
├─────────────────────────────────────────────────────────────┤
|
|
21
|
+
│ @eldrin-project/eldrin-app-core │
|
|
22
|
+
│ • Database migrations with checksum verification │
|
|
23
|
+
│ • JWT authentication & permission checking │
|
|
24
|
+
│ • Permission middleware for API routes │
|
|
25
|
+
│ • Event system for inter-app communication │
|
|
26
|
+
│ • Vite plugin for build-time SQL loading │
|
|
27
|
+
│ • CLI tools for marketplace release/submission │
|
|
28
|
+
├─────────────────────────────────────────────────────────────┤
|
|
29
|
+
│ Cloudflare Workers + D1 Database │
|
|
30
|
+
└─────────────────────────────────────────────────────────────┘
|
|
31
|
+
```
|
|
16
32
|
|
|
17
33
|
## Installation
|
|
18
34
|
|
|
@@ -20,142 +36,244 @@ Standard library for building Eldrin marketplace apps. Provides database migrati
|
|
|
20
36
|
npm install @eldrin-project/eldrin-app-core
|
|
21
37
|
```
|
|
22
38
|
|
|
23
|
-
##
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
### 1. Database Migrations
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
Run SQL migrations against Cloudflare D1 with automatic tracking, checksum verification, and rollback support.
|
|
26
44
|
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
import { createApp } from '@eldrin-project/eldrin-app-core';
|
|
30
|
-
import migrations from 'virtual:eldrin/migrations';
|
|
31
|
-
import App from './App';
|
|
45
|
+
```typescript
|
|
46
|
+
import { runMigrations, getMigrationStatus } from '@eldrin-project/eldrin-app-core';
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
// Run pending migrations
|
|
49
|
+
const result = await runMigrations(db, {
|
|
50
|
+
migrations: [
|
|
51
|
+
{ name: '20250101120000-create-users.sql', content: 'CREATE TABLE users...' },
|
|
52
|
+
{ name: '20250102100000-add-email.sql', content: 'ALTER TABLE users...' },
|
|
53
|
+
],
|
|
54
|
+
onLog: (msg, level) => console.log(`[${level}] ${msg}`),
|
|
37
55
|
});
|
|
38
|
-
```
|
|
39
56
|
|
|
40
|
-
|
|
57
|
+
if (result.success) {
|
|
58
|
+
console.log(`Executed ${result.executed} migrations`);
|
|
59
|
+
} else {
|
|
60
|
+
console.error(`Failed at ${result.error?.migration}: ${result.error?.message}`);
|
|
61
|
+
}
|
|
41
62
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
63
|
+
// Check migration status
|
|
64
|
+
const status = await getMigrationStatus(db, migrations);
|
|
65
|
+
console.log('Pending:', status.pending);
|
|
66
|
+
console.log('Executed:', status.executed.length);
|
|
67
|
+
```
|
|
46
68
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
**Migration file naming:** `YYYYMMDDHHMMSS-description.sql`
|
|
70
|
+
|
|
71
|
+
Migrations are tracked in `_eldrin_migrations` table with SHA-256 checksums to detect tampering.
|
|
72
|
+
|
|
73
|
+
### 2. Authentication & Authorization
|
|
74
|
+
|
|
75
|
+
Verify JWT tokens and check permissions in your Cloudflare Worker:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import {
|
|
79
|
+
verifyJWT,
|
|
80
|
+
requireJWTAuth,
|
|
81
|
+
requireJWTPermission,
|
|
82
|
+
hasPermission,
|
|
83
|
+
isPlatformAdmin,
|
|
84
|
+
} from '@eldrin-project/eldrin-app-core';
|
|
85
|
+
|
|
86
|
+
export default {
|
|
87
|
+
async fetch(request: Request, env: Env) {
|
|
88
|
+
// Option 1: Manual verification
|
|
89
|
+
const result = await verifyJWT(request, {
|
|
90
|
+
secret: env.JWT_SECRET,
|
|
91
|
+
appId: 'my-app',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!result.success) {
|
|
95
|
+
return Response.json({ error: result.error }, { status: 401 });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { auth } = result;
|
|
99
|
+
console.log('User:', auth.email);
|
|
100
|
+
console.log('Permissions:', auth.permissions);
|
|
101
|
+
|
|
102
|
+
// Option 2: Require auth (returns Response on failure)
|
|
103
|
+
const auth = await requireJWTAuth(request, {
|
|
104
|
+
secret: env.JWT_SECRET,
|
|
105
|
+
appId: 'my-app',
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (auth instanceof Response) return auth; // 401
|
|
109
|
+
|
|
110
|
+
// Option 3: Require specific permission
|
|
111
|
+
const auth = await requireJWTPermission(
|
|
112
|
+
request,
|
|
113
|
+
{ secret: env.JWT_SECRET, appId: 'my-app' },
|
|
114
|
+
'invoices',
|
|
115
|
+
'write'
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (auth instanceof Response) return auth; // 401 or 403
|
|
119
|
+
|
|
120
|
+
// Check permissions manually
|
|
121
|
+
if (hasPermission(auth, 'invoices', 'delete')) {
|
|
122
|
+
// User can delete invoices
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (isPlatformAdmin(auth)) {
|
|
126
|
+
// User is platform admin (has all permissions)
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
};
|
|
55
130
|
```
|
|
56
131
|
|
|
57
|
-
### 3.
|
|
132
|
+
### 3. Permission Middleware
|
|
58
133
|
|
|
59
|
-
|
|
60
|
-
// src/vite-env.d.ts
|
|
61
|
-
/// <reference types="@eldrin-project/eldrin-app-core/vite-env" />
|
|
62
|
-
```
|
|
134
|
+
Manifest-driven middleware that automatically enforces route permissions:
|
|
63
135
|
|
|
64
|
-
|
|
136
|
+
```typescript
|
|
137
|
+
import { createPermissionMiddleware } from '@eldrin-project/eldrin-app-core';
|
|
138
|
+
import manifest from './eldrin-app.manifest.json';
|
|
65
139
|
|
|
66
|
-
|
|
67
|
-
|
|
140
|
+
const middleware = createPermissionMiddleware<Env>({
|
|
141
|
+
manifest,
|
|
142
|
+
getSecret: (env) => env.JWT_SECRET,
|
|
143
|
+
cors: {
|
|
144
|
+
allowOrigin: '*',
|
|
145
|
+
allowMethods: 'GET, POST, PUT, DELETE, OPTIONS',
|
|
146
|
+
allowHeaders: 'Content-Type, Authorization',
|
|
147
|
+
},
|
|
148
|
+
});
|
|
68
149
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
150
|
+
export default {
|
|
151
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
152
|
+
const result = await middleware.handle(request, env);
|
|
72
153
|
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
.then(({ results }) => setInvoices(results));
|
|
78
|
-
}, [db]);
|
|
154
|
+
// Rejected request (401, 403, or CORS preflight)
|
|
155
|
+
if (result.response) {
|
|
156
|
+
return result.response;
|
|
157
|
+
}
|
|
79
158
|
|
|
80
|
-
|
|
159
|
+
// Authorized - access auth context and route params
|
|
160
|
+
const { auth, url, params } = result;
|
|
161
|
+
console.log('User:', auth.email);
|
|
162
|
+
console.log('Route params:', params); // e.g., { id: '123' }
|
|
163
|
+
|
|
164
|
+
// Handle the request...
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Manifest API configuration:**
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"id": "my-app",
|
|
174
|
+
"developer_id": "acme",
|
|
175
|
+
"api": {
|
|
176
|
+
"defaultPolicy": "deny",
|
|
177
|
+
"publicRoutes": ["/api/health"],
|
|
178
|
+
"routes": [
|
|
179
|
+
{ "method": "GET", "path": "/api/items", "permission": "items:read" },
|
|
180
|
+
{ "method": "POST", "path": "/api/items", "permission": "items:write" },
|
|
181
|
+
{ "method": "GET", "path": "/api/items/:id", "permission": "items:read" },
|
|
182
|
+
{ "method": "DELETE", "path": "/api/items/:id", "permission": "items:delete" }
|
|
183
|
+
]
|
|
184
|
+
}
|
|
81
185
|
}
|
|
82
186
|
```
|
|
83
187
|
|
|
84
|
-
|
|
188
|
+
### 4. Event System
|
|
85
189
|
|
|
86
|
-
|
|
190
|
+
Publish/subscribe events for inter-app communication:
|
|
87
191
|
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
├── 20250101120000-create-invoices.sql
|
|
91
|
-
├── 20250101120000-create-invoices.rollback.sql
|
|
92
|
-
└── 20250102100000-add-status-column.sql
|
|
93
|
-
```
|
|
192
|
+
```typescript
|
|
193
|
+
import { createEventClient } from '@eldrin-project/eldrin-app-core';
|
|
94
194
|
|
|
95
|
-
|
|
195
|
+
export default {
|
|
196
|
+
async fetch(request: Request, env: Env) {
|
|
197
|
+
const events = createEventClient(env, 'invoicing');
|
|
96
198
|
|
|
97
|
-
|
|
199
|
+
// Emit an event
|
|
200
|
+
await events.emit('invoice.created', {
|
|
201
|
+
invoiceId: 'inv_123',
|
|
202
|
+
customerId: 'cust_456',
|
|
203
|
+
total: 1500,
|
|
204
|
+
});
|
|
98
205
|
|
|
99
|
-
|
|
206
|
+
// Poll for events (in a scheduled worker)
|
|
207
|
+
const pending = await events.poll({ limit: 10 });
|
|
100
208
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
onMigrationError: (error) => {
|
|
110
|
-
console.error('Migration failed:', error);
|
|
209
|
+
for (const delivery of pending.events) {
|
|
210
|
+
try {
|
|
211
|
+
await processEvent(delivery.event);
|
|
212
|
+
await events.ack(delivery.id);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
await events.nack(delivery.id, error.message);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
111
217
|
},
|
|
112
|
-
}
|
|
218
|
+
};
|
|
113
219
|
```
|
|
114
220
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
### createApp(options)
|
|
221
|
+
**Declare events in manifest:**
|
|
118
222
|
|
|
119
|
-
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"events": {
|
|
226
|
+
"emits": ["invoice.created", "invoice.paid"],
|
|
227
|
+
"subscribes": ["payment.received", "customer.updated"]
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
120
231
|
|
|
121
|
-
|
|
122
|
-
|--------|------|-------------|
|
|
123
|
-
| `name` | `string` | Unique app identifier |
|
|
124
|
-
| `root` | `React.ComponentType` | Root React component |
|
|
125
|
-
| `migrations` | `MigrationFile[]` | Migration files (from virtual module) |
|
|
126
|
-
| `onMigrationsComplete` | `(result) => void` | Called after successful migrations |
|
|
127
|
-
| `onMigrationError` | `(error) => void` | Called if migrations fail |
|
|
232
|
+
### 5. Vite Plugin
|
|
128
233
|
|
|
129
|
-
|
|
234
|
+
Load SQL migrations at build time (Workers have no filesystem access at runtime):
|
|
130
235
|
|
|
131
|
-
|
|
236
|
+
```typescript
|
|
237
|
+
// vite.config.ts
|
|
238
|
+
import { defineConfig } from 'vite';
|
|
239
|
+
import { eldrinPlugin } from '@eldrin-project/eldrin-app-core/vite';
|
|
132
240
|
|
|
133
|
-
|
|
134
|
-
|
|
241
|
+
export default defineConfig({
|
|
242
|
+
plugins: [
|
|
243
|
+
eldrinPlugin({
|
|
244
|
+
migrationsDir: './migrations',
|
|
245
|
+
seedsDir: './seeds',
|
|
246
|
+
}),
|
|
247
|
+
],
|
|
248
|
+
});
|
|
135
249
|
```
|
|
136
250
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
```tsx
|
|
142
|
-
const { db, migrationsComplete, migrationResult } = useDatabaseContext();
|
|
251
|
+
```typescript
|
|
252
|
+
// Add type declarations
|
|
253
|
+
// src/vite-env.d.ts
|
|
254
|
+
/// <reference types="@eldrin-project/eldrin-app-core/vite-env" />
|
|
143
255
|
```
|
|
144
256
|
|
|
145
|
-
|
|
257
|
+
```typescript
|
|
258
|
+
// Import migrations in your code
|
|
259
|
+
import migrations from 'virtual:eldrin/migrations';
|
|
260
|
+
import seeds from 'virtual:eldrin/seeds';
|
|
261
|
+
```
|
|
146
262
|
|
|
147
|
-
|
|
263
|
+
## Package Exports
|
|
148
264
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
265
|
+
| Export | Environment | Description |
|
|
266
|
+
|--------|-------------|-------------|
|
|
267
|
+
| `@eldrin-project/eldrin-app-core` | Browser/Worker | Main exports (migrations, auth, events, middleware) |
|
|
268
|
+
| `@eldrin-project/eldrin-app-core/node` | Node.js only | Marketplace utilities (generateMigrationManifest) |
|
|
269
|
+
| `@eldrin-project/eldrin-app-core/vite` | Node.js only | Vite plugin for loading SQL files |
|
|
270
|
+
| `@eldrin-project/eldrin-app-core/vite-env` | Types only | TypeScript declarations for virtual modules |
|
|
153
271
|
|
|
154
272
|
## CLI Tools
|
|
155
273
|
|
|
156
274
|
### eldrin-release
|
|
157
275
|
|
|
158
|
-
Prepares your app for marketplace submission
|
|
276
|
+
Prepares your app for marketplace submission:
|
|
159
277
|
|
|
160
278
|
```bash
|
|
161
279
|
npx eldrin-release [options]
|
|
@@ -168,18 +286,25 @@ Options:
|
|
|
168
286
|
-o, --output <path> Output directory
|
|
169
287
|
```
|
|
170
288
|
|
|
171
|
-
Creates a release package at `dist/release/{developerId}/{appId}/v{version}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
289
|
+
Creates a release package at `dist/release/{developerId}/{appId}/v{version}/`:
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
dist/release/acme/invoicing/
|
|
293
|
+
├── migrations/ # Shared across versions
|
|
294
|
+
│ ├── index.json # Manifest with checksums
|
|
295
|
+
│ └── *.sql # Migration files
|
|
296
|
+
└── v1.0.0/
|
|
297
|
+
├── eldrin-app.manifest.json # App metadata
|
|
298
|
+
├── bundle.js # Worker bundle
|
|
299
|
+
└── deploy-bundle.json # Worker + client assets
|
|
300
|
+
```
|
|
176
301
|
|
|
177
302
|
### eldrin-submit
|
|
178
303
|
|
|
179
|
-
Submits a release to the Eldrin marketplace
|
|
304
|
+
Submits a release to the Eldrin marketplace:
|
|
180
305
|
|
|
181
306
|
```bash
|
|
182
|
-
npx eldrin-submit -d ./dist/release/
|
|
307
|
+
npx eldrin-submit -d ./dist/release/acme/invoicing/v1.0.0
|
|
183
308
|
|
|
184
309
|
Options:
|
|
185
310
|
-d, --release-dir <dir> Release directory to submit
|
|
@@ -188,7 +313,7 @@ Options:
|
|
|
188
313
|
--dry-run Preview without submitting
|
|
189
314
|
```
|
|
190
315
|
|
|
191
|
-
Environment variables
|
|
316
|
+
**Environment variables:**
|
|
192
317
|
- `ELDRIN_API_KEY` - Your developer API key
|
|
193
318
|
- `ELDRIN_DEVELOPER_ID` - Your developer ID
|
|
194
319
|
- `ELDRIN_MARKETPLACE_URL` - Marketplace URL (default: https://eldrin.io)
|
|
@@ -199,11 +324,11 @@ Create `.eldrinrc.json` in your project root:
|
|
|
199
324
|
|
|
200
325
|
```json
|
|
201
326
|
{
|
|
202
|
-
"developerId": "
|
|
327
|
+
"developerId": "acme",
|
|
203
328
|
"marketplaceUrl": "https://eldrin.io",
|
|
204
|
-
"manifestPath": "./eldrin-app.manifest.json",
|
|
329
|
+
"manifestPath": "./public/eldrin-app.manifest.json",
|
|
205
330
|
"migrationsDir": "./migrations",
|
|
206
|
-
"buildCommand": "npm run build
|
|
331
|
+
"buildCommand": "npm run build",
|
|
207
332
|
"clientDir": "dist/client",
|
|
208
333
|
"workerEntry": "dist/worker/index.js"
|
|
209
334
|
}
|
|
@@ -215,34 +340,99 @@ Create `eldrin-app.manifest.json`:
|
|
|
215
340
|
|
|
216
341
|
```json
|
|
217
342
|
{
|
|
218
|
-
"id": "
|
|
219
|
-
"name": "
|
|
343
|
+
"id": "invoicing",
|
|
344
|
+
"name": "Invoicing App",
|
|
220
345
|
"version": "1.0.0",
|
|
221
|
-
"description": "
|
|
222
|
-
"
|
|
223
|
-
|
|
224
|
-
"
|
|
346
|
+
"description": "Create and manage invoices",
|
|
347
|
+
"developer_id": "acme",
|
|
348
|
+
"permissions": [
|
|
349
|
+
{ "resource": "invoices", "actions": ["read", "write", "delete"] },
|
|
350
|
+
{ "resource": "customers", "actions": ["read"] }
|
|
351
|
+
],
|
|
352
|
+
"groups": [
|
|
353
|
+
{
|
|
354
|
+
"id": "admin",
|
|
355
|
+
"name": "Invoice Admin",
|
|
356
|
+
"permissions": ["invoices:*", "customers:read"]
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"id": "viewer",
|
|
360
|
+
"name": "Viewer",
|
|
361
|
+
"permissions": ["invoices:read", "customers:read"]
|
|
362
|
+
}
|
|
363
|
+
],
|
|
364
|
+
"events": {
|
|
365
|
+
"emits": ["invoice.created", "invoice.paid"],
|
|
366
|
+
"subscribes": ["payment.received"]
|
|
225
367
|
},
|
|
226
|
-
"
|
|
368
|
+
"api": {
|
|
369
|
+
"defaultPolicy": "deny",
|
|
370
|
+
"publicRoutes": ["/api/health"],
|
|
371
|
+
"routes": [
|
|
372
|
+
{ "method": "GET", "path": "/api/invoices", "permission": "invoices:read" },
|
|
373
|
+
{ "method": "POST", "path": "/api/invoices", "permission": "invoices:write" }
|
|
374
|
+
]
|
|
375
|
+
}
|
|
227
376
|
}
|
|
228
377
|
```
|
|
229
378
|
|
|
230
|
-
##
|
|
379
|
+
## Framework Adapters
|
|
231
380
|
|
|
232
|
-
|
|
233
|
-
- React 18.x or 19.x
|
|
234
|
-
- Vite 6.x or 7.x
|
|
381
|
+
This core package is framework-agnostic. Use these adapters for framework-specific integrations:
|
|
235
382
|
|
|
236
|
-
|
|
383
|
+
| Framework | Package | Features |
|
|
384
|
+
|-----------|---------|----------|
|
|
385
|
+
| Angular | `@eldrin-project/eldrin-app-angular` | InjectionTokens, providers, single-spa lifecycle |
|
|
386
|
+
| React | `@eldrin-project/eldrin-app-react` | Hooks (useDatabase), Context, single-spa lifecycle |
|
|
387
|
+
| Vue | `@eldrin-project/eldrin-app-vue` | Composables, provide/inject, single-spa lifecycle |
|
|
237
388
|
|
|
238
|
-
|
|
389
|
+
## API Reference
|
|
239
390
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
391
|
+
### Migrations
|
|
392
|
+
|
|
393
|
+
| Function | Description |
|
|
394
|
+
|----------|-------------|
|
|
395
|
+
| `runMigrations(db, options)` | Execute pending migrations |
|
|
396
|
+
| `getMigrationStatus(db, migrations)` | Get pending/executed/orphaned migrations |
|
|
397
|
+
| `rollbackMigrations(db, options)` | Rollback migrations (with .rollback.sql files) |
|
|
398
|
+
| `calculateChecksum(content)` | Calculate SHA-256 checksum of migration content |
|
|
399
|
+
| `verifyChecksum(content, checksum)` | Verify content matches stored checksum |
|
|
400
|
+
|
|
401
|
+
### Auth
|
|
402
|
+
|
|
403
|
+
| Function | Description |
|
|
404
|
+
|----------|-------------|
|
|
405
|
+
| `verifyJWT(request, options)` | Verify JWT and extract auth context |
|
|
406
|
+
| `getAuthContextFromJWT(request, options)` | Get auth context (JWT or proxy headers) |
|
|
407
|
+
| `requireJWTAuth(request, options)` | Require valid JWT (returns Response on failure) |
|
|
408
|
+
| `requireJWTPermission(request, options, resource, action)` | Require specific permission |
|
|
409
|
+
| `hasPermission(auth, resource, action)` | Check if auth has permission |
|
|
410
|
+
| `isPlatformAdmin(auth)` | Check if auth is platform admin |
|
|
411
|
+
|
|
412
|
+
### Middleware
|
|
413
|
+
|
|
414
|
+
| Function | Description |
|
|
415
|
+
|----------|-------------|
|
|
416
|
+
| `createPermissionMiddleware(config)` | Create manifest-driven permission middleware |
|
|
417
|
+
| `compileRoutes(routes, prefix)` | Pre-compile route patterns for matching |
|
|
418
|
+
| `matchRoute(method, path, routes)` | Match request against compiled routes |
|
|
419
|
+
|
|
420
|
+
### Events
|
|
421
|
+
|
|
422
|
+
| Class/Function | Description |
|
|
423
|
+
|----------------|-------------|
|
|
424
|
+
| `EldrinEventClient` | Client for pub/sub event communication |
|
|
425
|
+
| `createEventClient(env, appId)` | Create event client from worker env |
|
|
426
|
+
| `client.emit(type, payload)` | Emit an event |
|
|
427
|
+
| `client.poll(options)` | Poll for pending events |
|
|
428
|
+
| `client.ack(deliveryId)` | Acknowledge successful processing |
|
|
429
|
+
| `client.nack(deliveryId, error)` | Negative acknowledge (retry later) |
|
|
430
|
+
|
|
431
|
+
## Requirements
|
|
432
|
+
|
|
433
|
+
- Node.js 18.0.0 or later
|
|
434
|
+
- Vite 6.x or 7.x (optional, for Vite plugin)
|
|
245
435
|
|
|
246
436
|
## License
|
|
247
437
|
|
|
248
|
-
MIT
|
|
438
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var promises = require('fs/promises');
|
|
4
|
-
var fs = require('fs');
|
|
5
|
-
var path = require('path');
|
|
6
|
-
|
|
7
3
|
// src/migrations/checksum.ts
|
|
8
4
|
var CHECKSUM_PREFIX = "sha256:";
|
|
9
5
|
async function calculateChecksum(content, options = {}) {
|
|
@@ -371,75 +367,6 @@ async function rollbackMigrations(db, options) {
|
|
|
371
367
|
};
|
|
372
368
|
}
|
|
373
369
|
}
|
|
374
|
-
async function readMigrationFiles(dir) {
|
|
375
|
-
if (!fs.existsSync(dir)) {
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
const files = await promises.readdir(dir);
|
|
379
|
-
const sqlFiles = files.filter((f) => isValidMigrationFilename(f)).sort();
|
|
380
|
-
const migrations = [];
|
|
381
|
-
for (const file of sqlFiles) {
|
|
382
|
-
const content = await promises.readFile(path.resolve(dir, file), "utf-8");
|
|
383
|
-
migrations.push({
|
|
384
|
-
name: path.basename(file),
|
|
385
|
-
content
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
return migrations;
|
|
389
|
-
}
|
|
390
|
-
async function generateMigrationManifest(options) {
|
|
391
|
-
const { migrationsDir, database } = options;
|
|
392
|
-
const files = await readMigrationFiles(migrationsDir);
|
|
393
|
-
const migrations = await Promise.all(
|
|
394
|
-
files.map(async (file) => {
|
|
395
|
-
const timestamp = extractTimestamp(file.name);
|
|
396
|
-
const checksum = await calculatePrefixedChecksum(file.content);
|
|
397
|
-
return {
|
|
398
|
-
id: timestamp || file.name.replace(".sql", ""),
|
|
399
|
-
file: file.name,
|
|
400
|
-
checksum
|
|
401
|
-
};
|
|
402
|
-
})
|
|
403
|
-
);
|
|
404
|
-
return {
|
|
405
|
-
manifest: {
|
|
406
|
-
database,
|
|
407
|
-
migrations
|
|
408
|
-
},
|
|
409
|
-
files
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
async function validateMigrationManifest(manifest, files) {
|
|
413
|
-
const errors = [];
|
|
414
|
-
for (const entry of manifest.migrations) {
|
|
415
|
-
const file = files.find((f) => f.name === entry.file);
|
|
416
|
-
if (!file) {
|
|
417
|
-
errors.push(`Missing file: ${entry.file}`);
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
const actualChecksum = await calculatePrefixedChecksum(file.content);
|
|
421
|
-
if (actualChecksum !== entry.checksum) {
|
|
422
|
-
errors.push(
|
|
423
|
-
`Checksum mismatch for ${entry.file}: expected ${entry.checksum}, got ${actualChecksum}`
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
const timestamp = extractTimestamp(file.name);
|
|
427
|
-
if (timestamp !== entry.id) {
|
|
428
|
-
errors.push(
|
|
429
|
-
`ID mismatch for ${entry.file}: expected ${timestamp}, got ${entry.id}`
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
for (const file of files) {
|
|
434
|
-
if (!manifest.migrations.some((m) => m.file === file.name)) {
|
|
435
|
-
errors.push(`File not in manifest: ${file.name}`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
valid: errors.length === 0,
|
|
440
|
-
errors
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
370
|
|
|
444
371
|
// src/events/client.ts
|
|
445
372
|
var EldrinEventClient = class {
|
|
@@ -1052,7 +979,6 @@ exports.compileRoutes = compileRoutes;
|
|
|
1052
979
|
exports.createEventClient = createEventClient;
|
|
1053
980
|
exports.createPermissionMiddleware = createPermissionMiddleware;
|
|
1054
981
|
exports.extractTimestamp = extractTimestamp;
|
|
1055
|
-
exports.generateMigrationManifest = generateMigrationManifest;
|
|
1056
982
|
exports.getAuthContext = getAuthContext;
|
|
1057
983
|
exports.getAuthContextFromJWT = getAuthContextFromJWT;
|
|
1058
984
|
exports.getMigrationStatus = getMigrationStatus;
|
|
@@ -1073,7 +999,6 @@ exports.requireJWTPermission = requireJWTPermission;
|
|
|
1073
999
|
exports.requirePermission = requirePermission;
|
|
1074
1000
|
exports.rollbackMigrations = rollbackMigrations;
|
|
1075
1001
|
exports.runMigrations = runMigrations;
|
|
1076
|
-
exports.validateMigrationManifest = validateMigrationManifest;
|
|
1077
1002
|
exports.verifyChecksum = verifyChecksum;
|
|
1078
1003
|
exports.verifyJWT = verifyJWT;
|
|
1079
1004
|
//# sourceMappingURL=index.cjs.map
|