@server/next 0.37.0 → 0.37.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.
Files changed (3) hide show
  1. package/index.d.ts +8 -6
  2. package/index.js +118 -99
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -10,10 +10,8 @@ declare class UploadPipeline {
10
10
  private _limits;
11
11
  constructor(bucket?: Bucket | string | null);
12
12
  limit(options: LimitOptions): this;
13
- store(bucket: Bucket | string): this;
14
13
  processFile(originalName: string, data: Buffer, contentType: string): Promise<UploadedFile>;
15
14
  }
16
- declare function upload(bucket?: Bucket | string | null): UploadPipeline;
17
15
 
18
16
  type CookieOptions = string | string[] | Cookie | Cookie[] | null;
19
17
  interface ResponseData {
@@ -31,7 +29,7 @@ declare class Reply$1 {
31
29
  cookies(key: string | Record<string, CookieOptions>, value?: CookieOptions): this;
32
30
  json(body: unknown): Response;
33
31
  redirect(path: string): Response;
34
- file(path: string): Promise<Response>;
32
+ file(path: string | BucketFile): Promise<Response>;
35
33
  send(body?: string | Buffer | ReadableStream | any): Response;
36
34
  }
37
35
  type Params<K extends keyof Reply$1> = Reply$1[K] extends (...args: infer A) => any ? A : never;
@@ -99,6 +97,7 @@ type BucketFile = {
99
97
  readonly path: string;
100
98
  readonly id: string;
101
99
  readonly name: string;
100
+ readonly type?: string;
102
101
  exists(): Promise<boolean>;
103
102
  info?(): Promise<FileInfo>;
104
103
  write(content: string | Buffer | ReadableStream, options?: {
@@ -120,6 +119,9 @@ type UploadedFile = {
120
119
  type: string;
121
120
  size: number;
122
121
  };
122
+ type UploadOptions = LimitOptions & {
123
+ bucket: string | Bucket;
124
+ };
123
125
  type CorsSettings = {
124
126
  origin: string | boolean;
125
127
  methods: string;
@@ -209,7 +211,7 @@ type Options = {
209
211
  port?: number;
210
212
  secret?: string;
211
213
  public?: string | Bucket;
212
- uploads?: string | Bucket | UploadPipeline;
214
+ uploads?: string | Bucket | UploadOptions;
213
215
  store?: KVStore;
214
216
  cookies?: KVStore;
215
217
  session?: KVStore | {
@@ -301,7 +303,7 @@ type Context<Params extends Record<string, string | undefined> = Record<string,
301
303
  };
302
304
  app: Server;
303
305
  };
304
- type InlineReply = Response | Reply | {
306
+ type InlineReply = Response | Reply | BucketFile | {
305
307
  body: string;
306
308
  headers?: Headers;
307
309
  } | SerializableValue | JSX.Element | Buffer | ReadableStream;
@@ -504,4 +506,4 @@ declare class Server<O extends ServerConfig = {}> extends Router<O> {
504
506
  }
505
507
  declare function server<Session extends Record<string, any> = {}, User extends Record<string, any> = {}>(options?: Options): Server<ServerConfig<Session, User>>;
506
508
 
507
- export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type BodyOption, type Bucket, type BucketFile, type BunEnv, type CacheOption, type Context, type Cookie, type CorsSettings, type ExtractPathParams, type FileInfo, type InferParamType, type InlineReply, type KVStore, type LimitOptions, type LogLevel, type Logger, type Method, type Middleware, type Options, type ParamTypeMap, type ParamsToObject, type PathToParams, type Platform, type Provider, type Route, type RouteOptions, type RouterMethod, type SecurityOptions, type SecuritySettings, type SerializableValue, Server, type ServerConfig, TypedServerError as ServerError, type Settings, type Strategy, type Time, UploadPipeline, type UploadedFile, cache, cookies, server as default, download, file, headers, json, redirect, router, send, status, type, upload };
509
+ export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type BodyOption, type Bucket, type BucketFile, type BunEnv, type CacheOption, type Context, type Cookie, type CorsSettings, type ExtractPathParams, type FileInfo, type InferParamType, type InlineReply, type KVStore, type LogLevel, type Logger, type Method, type Middleware, type Options, type ParamTypeMap, type ParamsToObject, type PathToParams, type Platform, type Provider, type Route, type RouteOptions, type RouterMethod, type SecurityOptions, type SecuritySettings, type SerializableValue, Server, type ServerConfig, TypedServerError as ServerError, type Settings, type Strategy, type Time, type UploadOptions, type UploadedFile, cache, cookies, server as default, download, file, headers, json, redirect, router, send, status, type };
package/index.js CHANGED
@@ -102,6 +102,90 @@ var StatusError = class extends Error {
102
102
  import * as fs from "fs";
103
103
  import * as fsp from "fs/promises";
104
104
  import * as path from "path";
105
+
106
+ // src/helpers/mimes.ts
107
+ var mimes_default = {
108
+ aac: "audio/aac",
109
+ abw: "application/x-abiword",
110
+ arc: "application/x-freearc",
111
+ avif: "image/avif",
112
+ avi: "video/x-msvideo",
113
+ azw: "application/vnd.amazon.ebook",
114
+ bin: "application/octet-stream",
115
+ bmp: "image/bmp",
116
+ bz: "application/x-bzip",
117
+ bz2: "application/x-bzip2",
118
+ cda: "application/x-cdf",
119
+ csh: "application/x-csh",
120
+ css: "text/css",
121
+ csv: "text/csv",
122
+ doc: "application/msword",
123
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
124
+ eot: "application/vnd.ms-fontobject",
125
+ epub: "application/epub+zip",
126
+ gz: "application/gzip",
127
+ gif: "image/gif",
128
+ htm: "text/html",
129
+ html: "text/html",
130
+ ico: "image/vnd.microsoft.icon",
131
+ ics: "text/calendar",
132
+ jar: "application/java-archive",
133
+ jpeg: "image/jpeg",
134
+ jpg: "image/jpeg",
135
+ js: "text/javascript",
136
+ json: "application/json",
137
+ jsonld: "application/ld+json",
138
+ md: "text/markdown",
139
+ mid: "audio/midi",
140
+ midi: "audio/midi",
141
+ mjs: "text/javascript",
142
+ mp3: "audio/mpeg",
143
+ mp4: "video/mp4",
144
+ mpeg: "video/mpeg",
145
+ mpkg: "application/vnd.apple.installer+xml",
146
+ odp: "application/vnd.oasis.opendocument.presentation",
147
+ ods: "application/vnd.oasis.opendocument.spreadsheet",
148
+ odt: "application/vnd.oasis.opendocument.text",
149
+ oga: "audio/ogg",
150
+ ogv: "video/ogg",
151
+ ogx: "application/ogg",
152
+ opus: "audio/opus",
153
+ otf: "font/otf",
154
+ png: "image/png",
155
+ pdf: "application/pdf",
156
+ php: "application/x-httpd-php",
157
+ ppt: "application/vnd.ms-powerpoint",
158
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
159
+ rar: "application/vnd.rar",
160
+ rtf: "application/rtf",
161
+ sh: "application/x-sh",
162
+ svg: "image/svg+xml",
163
+ tar: "application/x-tar",
164
+ text: "text/plain",
165
+ tif: "image/tiff",
166
+ tiff: "image/tiff",
167
+ ts: "video/mp2t",
168
+ ttf: "font/ttf",
169
+ txt: "text/plain",
170
+ vsd: "application/vnd.visio",
171
+ wav: "audio/wav",
172
+ weba: "audio/webm",
173
+ webm: "video/webm",
174
+ webp: "image/webp",
175
+ woff: "font/woff",
176
+ woff2: "font/woff2",
177
+ xhtml: "application/xhtml+xml",
178
+ xls: "application/vnd.ms-excel",
179
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
180
+ xml: "application/xml",
181
+ xul: "application/vnd.mozilla.xul+xml",
182
+ zip: "application/zip",
183
+ "3gp": "video/3gpp",
184
+ "3g2": "video/3gpp2",
185
+ "7z": "application/x-7z-compressed"
186
+ };
187
+
188
+ // src/helpers/bucket.ts
105
189
  function localBucket(root) {
106
190
  const base = path.resolve(root);
107
191
  const resolveKey = (name) => {
@@ -114,6 +198,7 @@ function localBucket(root) {
114
198
  };
115
199
  const file2 = (name, win) => {
116
200
  const full = resolveKey(name);
201
+ const type2 = mimes_default[path.extname(name).slice(1).toLowerCase()];
117
202
  const read = () => {
118
203
  let opts;
119
204
  if (win) {
@@ -136,6 +221,7 @@ function localBucket(root) {
136
221
  path: full,
137
222
  id: name.replace(/^\/+/, ""),
138
223
  name: path.basename(name),
224
+ type: type2,
139
225
  async exists() {
140
226
  const stats = await fsp.stat(full).catch(() => null);
141
227
  return !!stats?.isFile();
@@ -145,7 +231,7 @@ function localBucket(root) {
145
231
  const exists = !!stats?.isFile();
146
232
  const total = stats?.size ?? 0;
147
233
  const size = win ? Math.max(0, Math.min(win.end, total) - win.start) : total;
148
- return { exists, size, date: stats?.mtime ?? null };
234
+ return { exists, size, date: stats?.mtime ?? null, type: type2 };
149
235
  },
150
236
  // Read-only view of [start, end), composed relative to the current window.
151
237
  slice(start, end) {
@@ -279,10 +365,6 @@ var UploadPipeline = class {
279
365
  this._limits = { ...this._limits, ...options };
280
366
  return this;
281
367
  }
282
- store(bucket2) {
283
- this._bucket = bucket(bucket2);
284
- return this;
285
- }
286
368
  async processFile(originalName, data, contentType) {
287
369
  const { maxSize, minSize, fileType } = this._limits;
288
370
  if (maxSize !== void 0 && data.length > parseBytes(maxSize)) {
@@ -308,16 +390,11 @@ var UploadPipeline = class {
308
390
  }
309
391
  }
310
392
  if (!this._bucket) {
311
- throw new Error(
312
- `No destination configured. Pass a bucket to upload() or call .store()`
313
- );
393
+ throw new Error(`No upload destination configured (missing bucket)`);
314
394
  }
315
395
  return saveFileToBucket(originalName, data, this._bucket, contentType);
316
396
  }
317
397
  };
318
- function upload(bucket2) {
319
- return new UploadPipeline(bucket2);
320
- }
321
398
 
322
399
  // src/helpers/bodyLimit.ts
323
400
  var INF = Number.POSITIVE_INFINITY;
@@ -339,88 +416,6 @@ var tooLarge = (max) => new StatusError(
339
416
  413
340
417
  );
341
418
 
342
- // src/helpers/mimes.ts
343
- var mimes_default = {
344
- aac: "audio/aac",
345
- abw: "application/x-abiword",
346
- arc: "application/x-freearc",
347
- avif: "image/avif",
348
- avi: "video/x-msvideo",
349
- azw: "application/vnd.amazon.ebook",
350
- bin: "application/octet-stream",
351
- bmp: "image/bmp",
352
- bz: "application/x-bzip",
353
- bz2: "application/x-bzip2",
354
- cda: "application/x-cdf",
355
- csh: "application/x-csh",
356
- css: "text/css",
357
- csv: "text/csv",
358
- doc: "application/msword",
359
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
360
- eot: "application/vnd.ms-fontobject",
361
- epub: "application/epub+zip",
362
- gz: "application/gzip",
363
- gif: "image/gif",
364
- htm: "text/html",
365
- html: "text/html",
366
- ico: "image/vnd.microsoft.icon",
367
- ics: "text/calendar",
368
- jar: "application/java-archive",
369
- jpeg: "image/jpeg",
370
- jpg: "image/jpeg",
371
- js: "text/javascript",
372
- json: "application/json",
373
- jsonld: "application/ld+json",
374
- md: "text/markdown",
375
- mid: "audio/midi",
376
- midi: "audio/midi",
377
- mjs: "text/javascript",
378
- mp3: "audio/mpeg",
379
- mp4: "video/mp4",
380
- mpeg: "video/mpeg",
381
- mpkg: "application/vnd.apple.installer+xml",
382
- odp: "application/vnd.oasis.opendocument.presentation",
383
- ods: "application/vnd.oasis.opendocument.spreadsheet",
384
- odt: "application/vnd.oasis.opendocument.text",
385
- oga: "audio/ogg",
386
- ogv: "video/ogg",
387
- ogx: "application/ogg",
388
- opus: "audio/opus",
389
- otf: "font/otf",
390
- png: "image/png",
391
- pdf: "application/pdf",
392
- php: "application/x-httpd-php",
393
- ppt: "application/vnd.ms-powerpoint",
394
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
395
- rar: "application/vnd.rar",
396
- rtf: "application/rtf",
397
- sh: "application/x-sh",
398
- svg: "image/svg+xml",
399
- tar: "application/x-tar",
400
- text: "text/plain",
401
- tif: "image/tiff",
402
- tiff: "image/tiff",
403
- ts: "video/mp2t",
404
- ttf: "font/ttf",
405
- txt: "text/plain",
406
- vsd: "application/vnd.visio",
407
- wav: "audio/wav",
408
- weba: "audio/webm",
409
- webm: "video/webm",
410
- webp: "image/webp",
411
- woff: "font/woff",
412
- woff2: "font/woff2",
413
- xhtml: "application/xhtml+xml",
414
- xls: "application/vnd.ms-excel",
415
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
416
- xml: "application/xml",
417
- xul: "application/vnd.mozilla.xul+xml",
418
- zip: "application/zip",
419
- "3gp": "video/3gpp",
420
- "3g2": "video/3gpp2",
421
- "7z": "application/x-7z-compressed"
422
- };
423
-
424
419
  // src/helpers/parseBody.ts
425
420
  function getBoundary(header) {
426
421
  if (!header) return null;
@@ -897,6 +892,10 @@ var Reply = class {
897
892
  return this.headers("location", path2).status(302).send();
898
893
  }
899
894
  async file(path2) {
895
+ if (typeof path2 !== "string") {
896
+ if (!await path2.exists()) return this.status(404).send();
897
+ return this.type(path2.type).send(path2.stream());
898
+ }
900
899
  try {
901
900
  const fs2 = await import("fs");
902
901
  const ext2 = path2.split(".").pop();
@@ -925,7 +924,7 @@ var Reply = class {
925
924
  return new Response(body, { status: status2, headers: headers2 });
926
925
  }
927
926
  const name = body?.constructor?.name;
928
- if (name === "Buffer") {
927
+ if (body instanceof Uint8Array) {
929
928
  if (!headers2.has("content-length")) {
930
929
  headers2.set("content-length", String(body.length));
931
930
  }
@@ -1721,9 +1720,20 @@ function config(options = {}) {
1721
1720
  }
1722
1721
  settings.cors = cors2;
1723
1722
  }
1724
- settings.public = options.public ? bucket(options.public) : null;
1725
- settings.uploads = options.uploads instanceof UploadPipeline ? options.uploads : options.uploads ? bucket(options.uploads) : null;
1726
- if (options.favicon) settings.favicon = options.favicon;
1723
+ const publicDir = options.public || env2.PUBLIC;
1724
+ settings.public = publicDir ? bucket(publicDir) : null;
1725
+ const up = options.uploads;
1726
+ if (!up) {
1727
+ settings.uploads = null;
1728
+ } else if (typeof up === "object" && "bucket" in up) {
1729
+ const { bucket: bucket2, maxSize, minSize, fileType } = up;
1730
+ const hasLimits = maxSize != null || minSize != null || fileType != null;
1731
+ settings.uploads = hasLimits ? new UploadPipeline(bucket2).limit({ maxSize, minSize, fileType }) : bucket(bucket2);
1732
+ } else {
1733
+ settings.uploads = bucket(up);
1734
+ }
1735
+ const favicon2 = options.favicon || env2.FAVICON;
1736
+ if (favicon2) settings.favicon = favicon2;
1727
1737
  settings.store = options.store ?? null;
1728
1738
  settings.cookies = options.cookies ?? null;
1729
1739
  if (options.session) {
@@ -1879,6 +1889,16 @@ async function parseResponse(out, ctx) {
1879
1889
  if (out instanceof Blob) {
1880
1890
  out = new Response(out, { headers: { "Content-Type": out.type } });
1881
1891
  }
1892
+ if (out && typeof out.stream === "function" && typeof out.bytes === "function" && typeof out.exists === "function" && typeof out.name === "string") {
1893
+ if (!await out.exists()) {
1894
+ out = new Response(null, { status: 404 });
1895
+ } else {
1896
+ out = new Response(
1897
+ out.stream(),
1898
+ out.type ? { headers: { "content-type": out.type } } : void 0
1899
+ );
1900
+ }
1901
+ }
1882
1902
  if (out instanceof ReadableStream) {
1883
1903
  out = new Response(out);
1884
1904
  }
@@ -3264,6 +3284,5 @@ export {
3264
3284
  router,
3265
3285
  send,
3266
3286
  status,
3267
- type,
3268
- upload
3287
+ type
3269
3288
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.37.0",
3
+ "version": "0.37.1",
4
4
  "description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
5
5
  "homepage": "https://server-js.com/",
6
6
  "repository": "github:franciscop/server-next",