@flink-app/static-files-plugin 0.12.1-alpha.3 → 0.12.1-alpha.35

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 (2) hide show
  1. package/package.json +4 -4
  2. package/readme.md +489 -21
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@flink-app/static-files-plugin",
3
- "version": "0.12.1-alpha.3",
3
+ "version": "0.12.1-alpha.35",
4
4
  "description": "Flink plugin that make flink serve static files",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\"",
7
- "prepublish": "tsc"
7
+ "prepare": "tsc"
8
8
  },
9
9
  "author": "johan@frost.se",
10
10
  "publishConfig": {
@@ -18,11 +18,11 @@
18
18
  "node-color-log": "^5.3.1"
19
19
  },
20
20
  "devDependencies": {
21
- "@flink-app/flink": "^0.12.1-alpha.3",
21
+ "@flink-app/flink": "^0.12.1-alpha.35",
22
22
  "@types/express": "4.17.13",
23
23
  "@types/node": "22.13.10",
24
24
  "ts-node": "^9.1.1",
25
25
  "typescript": "5.4.5"
26
26
  },
27
- "gitHead": "51b6524e233acb2430953ffaed6382909f34db8e"
27
+ "gitHead": "f8e8c6565a9ca1dd3e5fdb4c2a791c99ae3ba51a"
28
28
  }
package/readme.md CHANGED
@@ -1,54 +1,522 @@
1
- # Flink API Docs
1
+ # Static Files Plugin
2
2
 
3
- A FLINK plugin that makes it possible to serve static files in FLINK.
3
+ A Flink plugin for serving static files (HTML, CSS, JavaScript, images, etc.) through your Flink application using Express's built-in static file serving middleware.
4
4
 
5
- ## Usage
5
+ ## Installation
6
6
 
7
- Install plugin to your flink app project:
7
+ Install the plugin to your Flink app project:
8
8
 
9
- ```
10
- npm i -S @flink-app/static-files-plugin
9
+ ```bash
10
+ npm install @flink-app/static-files-plugin
11
11
  ```
12
12
 
13
- Add and configure plugin in your app startup (probable the `index.ts` in root project):
13
+ ## Configuration
14
14
 
15
- ```
15
+ ### Basic Setup
16
+
17
+ Configure the plugin to serve static files from a directory:
18
+
19
+ ```typescript
20
+ import { FlinkApp } from "@flink-app/flink";
16
21
  import { staticFilesPlugin } from "@flink-app/static-files-plugin";
22
+ import { join } from "path";
17
23
 
18
24
  function start() {
19
25
  new FlinkApp<AppContext>({
20
26
  name: "My app",
21
27
  plugins: [
22
- // Register plugin
23
- staticFilesPlugin({
24
- "path" : "/",
25
- "folder" : join(__dirname, "public")
26
- })
28
+ staticFilesPlugin({
29
+ path: "/", // URL path to serve files from
30
+ folder: join(__dirname, "public") // Filesystem path to static files
31
+ })
27
32
  ],
28
33
  }).start();
29
34
  }
35
+ ```
36
+
37
+ **Plugin Options:**
38
+
39
+ ```typescript
40
+ interface StaticOptions {
41
+ path: string; // Base URL path for static files (e.g., "/", "/assets", "/static")
42
+ folder: string; // Absolute path to the folder containing static files
43
+ }
44
+ ```
45
+
46
+ ## Usage Examples
47
+
48
+ ### Serve Files from Root Path
49
+
50
+ Serve static files directly from the root URL:
51
+
52
+ ```typescript
53
+ import { join } from "path";
54
+
55
+ staticFilesPlugin({
56
+ path: "/",
57
+ folder: join(__dirname, "public")
58
+ })
59
+
60
+ // Files accessible at:
61
+ // http://localhost:3000/index.html
62
+ // http://localhost:3000/styles.css
63
+ // http://localhost:3000/logo.png
64
+ ```
65
+
66
+ ### Serve Files from Subdirectory
67
+
68
+ Serve static files from a specific URL path:
30
69
 
70
+ ```typescript
71
+ import { join } from "path";
72
+
73
+ staticFilesPlugin({
74
+ path: "/assets",
75
+ folder: join(__dirname, "public")
76
+ })
77
+
78
+ // Files accessible at:
79
+ // http://localhost:3000/assets/index.html
80
+ // http://localhost:3000/assets/styles.css
81
+ // http://localhost:3000/assets/logo.png
82
+ ```
83
+
84
+ ### Multiple Static Directories
85
+
86
+ Serve different directories at different paths:
87
+
88
+ ```typescript
89
+ import { join } from "path";
90
+
91
+ new FlinkApp<AppContext>({
92
+ name: "My app",
93
+ plugins: [
94
+ // Serve images
95
+ staticFilesPlugin({
96
+ path: "/images",
97
+ folder: join(__dirname, "assets/images")
98
+ }),
99
+
100
+ // Serve CSS/JS
101
+ staticFilesPlugin({
102
+ path: "/static",
103
+ folder: join(__dirname, "assets/static")
104
+ }),
105
+
106
+ // Serve HTML pages
107
+ staticFilesPlugin({
108
+ path: "/",
109
+ folder: join(__dirname, "public")
110
+ })
111
+ ]
112
+ }).start();
113
+
114
+ // Files accessible at:
115
+ // http://localhost:3000/images/logo.png
116
+ // http://localhost:3000/static/app.js
117
+ // http://localhost:3000/index.html
31
118
  ```
32
119
 
33
- ### Copy files
120
+ ## File Types Supported
121
+
122
+ The plugin can serve any static file type, including:
34
123
 
35
- Flinks typescript compiler will package the app and run from inside the `dist/` folder. Only ts and json files are copied, so any static files needs to be copied manually.
124
+ - **HTML**: `.html`, `.htm`
125
+ - **CSS**: `.css`
126
+ - **JavaScript**: `.js`, `.mjs`
127
+ - **Images**: `.jpg`, `.jpeg`, `.png`, `.gif`, `.svg`, `.webp`, `.ico`
128
+ - **Fonts**: `.woff`, `.woff2`, `.ttf`, `.eot`
129
+ - **Documents**: `.pdf`, `.txt`, `.json`, `.xml`
130
+ - **Media**: `.mp4`, `.webm`, `.mp3`, `.ogg`, `.wav`
131
+ - And more...
36
132
 
37
- There are numerous ways to do that but one way is by using the `copyfiles` package:
133
+ ## Directory Structure Example
38
134
 
135
+ Typical project structure with static files:
39
136
 
40
137
  ```
41
- npm i -D copyfiles
138
+ my-flink-app/
139
+ ├── src/
140
+ │ ├── index.ts # App startup
141
+ │ ├── handlers/ # API handlers
142
+ │ └── public/ # Static files (source)
143
+ │ ├── index.html
144
+ │ ├── styles.css
145
+ │ ├── app.js
146
+ │ └── images/
147
+ │ └── logo.png
148
+ └── dist/ # Built output
149
+ └── src/
150
+ └── public/ # Static files (copied)
42
151
  ```
43
152
 
44
- Add following to package.json scripts
153
+ ## Copying Static Files to Dist
154
+
155
+ Flink's TypeScript compiler only copies `.ts` and `.json` files by default. Static files need to be copied manually to the `dist` folder.
156
+
157
+ ### Method 1: Using copyfiles Package
158
+
159
+ Install the `copyfiles` package:
45
160
 
161
+ ```bash
162
+ npm install --save-dev copyfiles
46
163
  ```
47
- ...
164
+
165
+ Add scripts to your `package.json`:
166
+
167
+ ```json
168
+ {
169
+ "scripts": {
48
170
  "copy-files": "copyfiles -u 1 src/public/**/* dist/src/",
49
171
  "predev": "npm run copy-files",
172
+ "prebuild": "npm run copy-files",
173
+ "dev": "nodemon",
174
+ "build": "tsc -p tsconfig.dist.json"
175
+ }
176
+ }
177
+ ```
178
+
179
+ **Explanation:**
180
+ - `copyfiles -u 1 src/public/**/* dist/src/` copies all files from `src/public/` to `dist/src/public/`
181
+ - `-u 1` removes the first directory level (strips `src/`)
182
+ - `predev` and `prebuild` run automatically before `dev` and `build` scripts
183
+
184
+ ### Method 2: Using npm-run-all
185
+
186
+ For parallel copying of multiple directories:
187
+
188
+ ```bash
189
+ npm install --save-dev npm-run-all copyfiles
190
+ ```
191
+
192
+ ```json
193
+ {
194
+ "scripts": {
195
+ "copy:public": "copyfiles -u 1 src/public/**/* dist/src/",
196
+ "copy:assets": "copyfiles -u 1 src/assets/**/* dist/src/",
197
+ "copy:all": "npm-run-all copy:*",
198
+ "predev": "npm run copy:all",
199
+ "prebuild": "npm run copy:all"
200
+ }
201
+ }
202
+ ```
203
+
204
+ ### Method 3: Using a Custom Script
205
+
206
+ Create a copy script (`scripts/copy-static.js`):
207
+
208
+ ```javascript
209
+ const fs = require("fs-extra");
210
+ const path = require("path");
211
+
212
+ const source = path.join(__dirname, "../src/public");
213
+ const dest = path.join(__dirname, "../dist/src/public");
214
+
215
+ fs.copySync(source, dest, {
216
+ overwrite: true,
217
+ errorOnExist: false
218
+ });
219
+
220
+ console.log("Static files copied successfully!");
221
+ ```
222
+
223
+ Add to `package.json`:
224
+
225
+ ```json
226
+ {
227
+ "scripts": {
228
+ "copy-files": "node scripts/copy-static.js",
229
+ "predev": "npm run copy-files",
50
230
  "prebuild": "npm run copy-files"
51
- ...
231
+ }
232
+ }
52
233
  ```
53
234
 
54
- This way all static files in the `src/public` folder will be copied into dist.
235
+ ## Complete Example
236
+
237
+ Here's a complete example of serving a frontend application:
238
+
239
+ ```typescript
240
+ import { FlinkApp } from "@flink-app/flink";
241
+ import { staticFilesPlugin } from "@flink-app/static-files-plugin";
242
+ import { join } from "path";
243
+ import { Ctx } from "./Ctx";
244
+
245
+ function start() {
246
+ new FlinkApp<Ctx>({
247
+ name: "My Full-Stack App",
248
+ db: {
249
+ uri: process.env.MONGODB_URI!
250
+ },
251
+ plugins: [
252
+ // Serve frontend application
253
+ staticFilesPlugin({
254
+ path: "/",
255
+ folder: join(__dirname, "public")
256
+ }),
257
+
258
+ // Serve uploaded files
259
+ staticFilesPlugin({
260
+ path: "/uploads",
261
+ folder: join(__dirname, "../uploads")
262
+ })
263
+ ]
264
+ }).start();
265
+ }
266
+
267
+ start();
268
+ ```
269
+
270
+ **Project Structure:**
271
+ ```
272
+ dist/
273
+ └── src/
274
+ ├── index.js # Compiled app
275
+ ├── public/ # Frontend files
276
+ │ ├── index.html
277
+ │ ├── app.js
278
+ │ └── styles.css
279
+ └── uploads/ # User uploads
280
+ └── image.jpg
281
+ ```
282
+
283
+ **Accessible URLs:**
284
+ - `http://localhost:3000/` → `dist/src/public/index.html`
285
+ - `http://localhost:3000/app.js` → `dist/src/public/app.js`
286
+ - `http://localhost:3000/styles.css` → `dist/src/public/styles.css`
287
+ - `http://localhost:3000/uploads/image.jpg` → `dist/src/uploads/image.jpg`
288
+
289
+ ## Advanced Usage
290
+
291
+ ### SPA (Single Page Application) Support
292
+
293
+ For React, Vue, Angular apps that use client-side routing:
294
+
295
+ ```typescript
296
+ import { FlinkApp } from "@flink-app/flink";
297
+ import { staticFilesPlugin } from "@flink-app/static-files-plugin";
298
+ import express from "express";
299
+ import { join } from "path";
300
+
301
+ const app = new FlinkApp<Ctx>({
302
+ name: "SPA App",
303
+ plugins: [
304
+ // Serve static assets
305
+ staticFilesPlugin({
306
+ path: "/",
307
+ folder: join(__dirname, "public")
308
+ })
309
+ ]
310
+ });
311
+
312
+ // Fallback to index.html for client-side routing
313
+ app.expressApp?.get("*", (req, res) => {
314
+ res.sendFile(join(__dirname, "public/index.html"));
315
+ });
316
+
317
+ app.start();
318
+ ```
319
+
320
+ ### Serving with Cache Headers
321
+
322
+ Modify Express static options for better caching:
323
+
324
+ ```typescript
325
+ import { FlinkApp } from "@flink-app/flink";
326
+ import express from "express";
327
+ import { join } from "path";
328
+
329
+ const app = new FlinkApp<Ctx>({
330
+ name: "My app",
331
+ plugins: []
332
+ });
333
+
334
+ // Manually configure express.static with options
335
+ const staticPath = join(__dirname, "public");
336
+ app.expressApp?.use("/", express.static(staticPath, {
337
+ maxAge: "1d", // Cache for 1 day
338
+ etag: true, // Enable ETags
339
+ lastModified: true, // Enable Last-Modified headers
340
+ index: ["index.html"] // Default index files
341
+ }));
342
+
343
+ app.start();
344
+ ```
345
+
346
+ ### Serving Different Files in Development vs Production
347
+
348
+ ```typescript
349
+ import { FlinkApp } from "@flink-app/flink";
350
+ import { staticFilesPlugin } from "@flink-app/static-files-plugin";
351
+ import { join } from "path";
352
+
353
+ const isDev = process.env.NODE_ENV !== "production";
354
+
355
+ new FlinkApp<Ctx>({
356
+ name: "My app",
357
+ plugins: [
358
+ staticFilesPlugin({
359
+ path: "/",
360
+ folder: isDev
361
+ ? join(__dirname, "../public") // Development: src/public
362
+ : join(__dirname, "public") // Production: dist/src/public
363
+ })
364
+ ]
365
+ }).start();
366
+ ```
367
+
368
+ ## Best Practices
369
+
370
+ ### 1. Use Absolute Paths
371
+
372
+ Always use `join(__dirname, ...)` for absolute paths:
373
+
374
+ ```typescript
375
+ // Good
376
+ folder: join(__dirname, "public")
377
+
378
+ // Bad - relative paths can cause issues
379
+ folder: "./public"
380
+ ```
381
+
382
+ ### 2. Order of Plugins Matters
383
+
384
+ Register API handlers before static file plugins to avoid conflicts:
385
+
386
+ ```typescript
387
+ new FlinkApp<Ctx>({
388
+ name: "My app",
389
+ plugins: [
390
+ apiDocsPlugin({ path: "/docs" }), // API endpoints first
391
+ staticFilesPlugin({ path: "/" }) // Static files last
392
+ ]
393
+ })
394
+ ```
395
+
396
+ If static files are registered first, they might intercept API routes.
397
+
398
+ ### 3. Use Subdirectories for Assets
399
+
400
+ Avoid serving from root in production:
401
+
402
+ ```typescript
403
+ // Development - convenient
404
+ staticFilesPlugin({
405
+ path: "/",
406
+ folder: join(__dirname, "public")
407
+ })
408
+
409
+ // Production - better organization
410
+ staticFilesPlugin({
411
+ path: "/static",
412
+ folder: join(__dirname, "public")
413
+ })
414
+ ```
415
+
416
+ ### 4. Security Considerations
417
+
418
+ - **Never serve sensitive files**: Don't put `.env`, config files, or database files in the static directory
419
+ - **Use proper permissions**: Ensure the static files directory has appropriate read permissions
420
+ - **Validate file paths**: The plugin uses Express's built-in static middleware which has path traversal protection
421
+
422
+ ### 5. Performance Optimization
423
+
424
+ For production, consider:
425
+ - Using a CDN for static assets
426
+ - Setting up Nginx/Apache as a reverse proxy to serve static files
427
+ - Enabling gzip/brotli compression at the web server level
428
+ - Using `express.static` options for cache headers
429
+
430
+ ## Common Issues
431
+
432
+ ### Files Not Loading
433
+
434
+ 1. **Check file path:**
435
+ ```typescript
436
+ console.log(join(__dirname, "public"));
437
+ // Verify this path exists
438
+ ```
439
+
440
+ 2. **Verify files were copied:**
441
+ ```bash
442
+ ls dist/src/public/
443
+ # Should show your static files
444
+ ```
445
+
446
+ 3. **Check URL path:**
447
+ ```typescript
448
+ // If path is "/assets"
449
+ staticFilesPlugin({ path: "/assets", folder: "..." })
450
+ // Access at: http://localhost:3000/assets/file.html
451
+ ```
452
+
453
+ ### 404 Errors
454
+
455
+ 1. **Case sensitivity**: File paths are case-sensitive on Linux/Mac
456
+ - `index.HTML` ≠ `index.html`
457
+
458
+ 2. **Path matching**: Ensure URL path matches configured path
459
+ ```typescript
460
+ // With path: "/static"
461
+ // ✓ http://localhost:3000/static/app.js
462
+ // ✗ http://localhost:3000/app.js
463
+ ```
464
+
465
+ ### Files Not Updating
466
+
467
+ 1. **Clear browser cache**: Hard refresh (Ctrl+Shift+R / Cmd+Shift+R)
468
+
469
+ 2. **Restart the server**: Changes to static files require restart unless using hot reload
470
+
471
+ 3. **Re-run copy script**: If files are in `src/`, make sure they're copied to `dist/`
472
+ ```bash
473
+ npm run copy-files
474
+ ```
475
+
476
+ ### MIME Type Issues
477
+
478
+ Express automatically sets correct MIME types. If you encounter issues:
479
+
480
+ ```typescript
481
+ import express from "express";
482
+ import { join } from "path";
483
+
484
+ app.expressApp?.use("/", express.static(join(__dirname, "public"), {
485
+ setHeaders: (res, path) => {
486
+ if (path.endsWith(".js")) {
487
+ res.setHeader("Content-Type", "application/javascript");
488
+ }
489
+ }
490
+ }));
491
+ ```
492
+
493
+ ## API Reference
494
+
495
+ ### Plugin Options
496
+
497
+ ```typescript
498
+ interface StaticOptions {
499
+ path: string; // Base URL path (must start with "/")
500
+ folder: string; // Absolute filesystem path to static files directory
501
+ }
502
+ ```
503
+
504
+ ### Plugin Function
505
+
506
+ ```typescript
507
+ function staticFilesPlugin(options: StaticOptions): FlinkPlugin
508
+ ```
509
+
510
+ **Returns:** A Flink plugin that registers Express static middleware.
511
+
512
+ **Logs:** When initialized, logs: `"Registered static file route {path}"`
513
+
514
+ ## Notes
515
+
516
+ - The plugin uses Express's `express.static` middleware internally
517
+ - Files are served with appropriate MIME types automatically
518
+ - Directory listings are not enabled by default (returns 404 for directories)
519
+ - The plugin does not add any context to `ctx.plugins`
520
+ - Multiple instances can be registered for different paths
521
+ - The `folder` path should be absolute (use `join(__dirname, ...)`)
522
+ - Static files are served with default cache headers (can be customized via Express static options)