@server/next 0.35.1 → 0.36.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.
Files changed (3) hide show
  1. package/index.d.ts +7 -1
  2. package/index.js +103 -67
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -34,11 +34,13 @@ type BodyOption = BodyMode | {
34
34
  mode?: BodyMode;
35
35
  max?: number | string | false;
36
36
  };
37
+ type CacheOption = string | number | false;
37
38
  type RouteOptions = {
38
39
  tags?: string | string[];
39
40
  title?: string;
40
41
  description?: string;
41
42
  body?: BodyOption;
43
+ cache?: CacheOption;
42
44
  };
43
45
  type Route = {
44
46
  path: string;
@@ -188,6 +190,7 @@ type Options = {
188
190
  favicon?: string | BucketFile;
189
191
  security?: boolean | SecurityOptions;
190
192
  body?: BodyOption;
193
+ cache?: CacheOption;
191
194
  };
192
195
  type Settings = {
193
196
  port: number;
@@ -207,6 +210,7 @@ type Settings = {
207
210
  favicon?: string | BucketFile;
208
211
  security: SecuritySettings;
209
212
  body: BodyOption;
213
+ cache?: CacheOption;
210
214
  };
211
215
  type Time = {
212
216
  (name: string): void;
@@ -345,6 +349,7 @@ declare class Reply {
345
349
  type(type?: string): this;
346
350
  download(name?: string): this;
347
351
  headers(key: string | Record<string, string>, value?: string): this;
352
+ cache(value: CacheOption): this;
348
353
  cookies(key: string | Record<string, CookieOptions>, value?: CookieOptions): this;
349
354
  json(body: unknown): Response;
350
355
  redirect(path: string): Response;
@@ -355,6 +360,7 @@ type Params<K extends keyof Reply> = Reply[K] extends (...args: infer A) => any
355
360
  declare const status: (...args: Params<"status">) => Reply;
356
361
  declare const headers: (...args: Params<"headers">) => Reply;
357
362
  declare const type: (...args: Params<"type">) => Reply;
363
+ declare const cache: (...args: Params<"cache">) => Reply;
358
364
  declare const download: (...args: Params<"download">) => Reply;
359
365
  declare const cookies: (...args: Params<"cookies">) => Reply;
360
366
  declare const send: (...args: Params<"send">) => Response;
@@ -494,4 +500,4 @@ declare class Server<O extends ServerConfig = {}> extends Router<O> {
494
500
  }
495
501
  declare function server<Session extends Record<string, any> = {}, User extends Record<string, any> = {}>(options?: Options): Server<ServerConfig<Session, User>>;
496
502
 
497
- export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type BodyMode, type BodyOption, type Bucket, type BucketFile, type BunEnv, 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, cookies, server as default, download, file, headers, json, redirect, router, send, status, type, upload };
503
+ 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 };
package/index.js CHANGED
@@ -683,6 +683,97 @@ async function resolveBody(ctx, body) {
683
683
  return parsed;
684
684
  }
685
685
 
686
+ // src/helpers/createCookies.ts
687
+ var EXPIRED = (/* @__PURE__ */ new Date(0)).toUTCString();
688
+ var times = /(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;
689
+ parse.millisecond = parse.ms = 1e-3;
690
+ parse.second = parse.sec = parse.s = parse[""] = 1;
691
+ parse.minute = parse.min = parse.m = parse.s * 60;
692
+ parse.hour = parse.hr = parse.h = parse.m * 60;
693
+ parse.day = parse.d = parse.h * 24;
694
+ parse.week = parse.wk = parse.w = parse.d * 7;
695
+ parse.year = parse.yr = parse.y = parse.d * 365.25;
696
+ parse.month = parse.b = parse.y / 12;
697
+ function parse(str) {
698
+ if (str === null || str === void 0) return null;
699
+ if (typeof str === "number") return str;
700
+ if (typeof str !== "string") {
701
+ throw new Error(`Not a string: ${str} (${typeof str})`);
702
+ }
703
+ str = str.toLowerCase().replace(/[,_]/g, "");
704
+ const [_, value, units] = times.exec(str) || [];
705
+ if (!units) return null;
706
+ const unitValue = parse[units] || parse[units.replace(/s$/, "")];
707
+ if (!unitValue) return null;
708
+ const result = unitValue * parseFloat(value);
709
+ return Math.abs(Math.round(result * 1e3));
710
+ }
711
+ function normalizeExpires(expires) {
712
+ if (expires === null || expires === void 0) return void 0;
713
+ if (expires === 0) return EXPIRED;
714
+ if (typeof expires === "string") {
715
+ if (/^[\d._]+\w+$/.test(expires)) {
716
+ return new Date(Date.now() + parse(expires)).toUTCString();
717
+ } else {
718
+ return expires;
719
+ }
720
+ }
721
+ if (typeof expires === "number") {
722
+ return new Date(Date.now() + expires).toUTCString();
723
+ }
724
+ if (expires instanceof Date) {
725
+ return expires.toUTCString();
726
+ }
727
+ return void 0;
728
+ }
729
+ function createCookies(key, val) {
730
+ if (val.value === null) val.expires = EXPIRED;
731
+ const { value, path: path2, expires, maxAge, httpOnly, secure, sameSite } = val;
732
+ let str = `${key}=${value || ""};Path=${path2 || "/"}`;
733
+ if (typeof expires !== "undefined") str += `;Expires=${normalizeExpires(expires)}`;
734
+ if (typeof maxAge === "number") str += `;Max-Age=${maxAge}`;
735
+ if (httpOnly) str += ";HttpOnly";
736
+ if (secure) str += ";Secure";
737
+ if (sameSite) str += `;SameSite=${sameSite}`;
738
+ return str;
739
+ }
740
+
741
+ // src/helpers/etag.ts
742
+ function etag(bytes) {
743
+ let h = 2166136261;
744
+ for (let i = 0; i < bytes.length; i++) {
745
+ h ^= bytes[i];
746
+ h = Math.imul(h, 16777619);
747
+ }
748
+ return `"${bytes.length.toString(16)}-${(h >>> 0).toString(16)}"`;
749
+ }
750
+
751
+ // src/helpers/cache.ts
752
+ function resolveCache(value) {
753
+ if (value === false || value === 0) return "no-store";
754
+ if (typeof value === "number") return `public, max-age=${Math.round(value)}`;
755
+ if (typeof value !== "string") return null;
756
+ const ms = parse(value);
757
+ return ms === null ? null : `public, max-age=${Math.round(ms / 1e3)}`;
758
+ }
759
+ async function applyCache(out, ctx) {
760
+ if (ctx.method !== "get" || out.status !== 200) return out;
761
+ if (!out.headers.has("cache-control")) {
762
+ const value = resolveCache(ctx.options.cache);
763
+ if (value) out.headers.set("cache-control", value);
764
+ }
765
+ if (out.headers.has("etag") || !out.headers.has("content-length")) return out;
766
+ const bytes = new Uint8Array(await out.arrayBuffer());
767
+ const tag = etag(bytes);
768
+ const headers2 = new Headers(out.headers);
769
+ headers2.set("etag", tag);
770
+ if (ctx.headers["if-none-match"] === tag) {
771
+ headers2.delete("content-length");
772
+ return new Response(null, { status: 304, headers: headers2 });
773
+ }
774
+ return new Response(bytes, { status: 200, headers: headers2 });
775
+ }
776
+
686
777
  // src/helpers/clientIp.ts
687
778
  var first = (v) => (Array.isArray(v) ? v[0] : v) || "";
688
779
  var normalize = (ip) => ip.replace(/^::ffff:/, "");
@@ -707,7 +798,7 @@ function isReadableStream(obj) {
707
798
  }
708
799
 
709
800
  // src/reply.ts
710
- var EXPIRED = (/* @__PURE__ */ new Date(0)).toUTCString();
801
+ var EXPIRED2 = (/* @__PURE__ */ new Date(0)).toUTCString();
711
802
  var Reply = class {
712
803
  res;
713
804
  constructor() {
@@ -743,6 +834,11 @@ var Reply = class {
743
834
  this.res.headers.append(key, value);
744
835
  return this;
745
836
  }
837
+ cache(value) {
838
+ const resolved = resolveCache(value);
839
+ if (resolved) this.res.headers.set("cache-control", resolved);
840
+ return this;
841
+ }
746
842
  cookies(key, value) {
747
843
  if (typeof key === "object") {
748
844
  Object.entries(key).map(([key2, value2]) => this.cookies(key2, value2));
@@ -752,7 +848,7 @@ var Reply = class {
752
848
  Object.values(value).map((val) => this.cookies(key, val));
753
849
  return this;
754
850
  }
755
- if (value === null) return this.cookies(key, { expires: EXPIRED });
851
+ if (value === null) return this.cookies(key, { expires: EXPIRED2 });
756
852
  if (typeof value !== "object") return this.cookies(key, { value });
757
853
  return this.headers("set-cookie", createCookies(key, value));
758
854
  }
@@ -822,6 +918,7 @@ var r = () => new Reply();
822
918
  var status = (...args) => r().status(...args);
823
919
  var headers = (...args) => r().headers(...args);
824
920
  var type = (...args) => r().type(...args);
921
+ var cache = (...args) => r().cache(...args);
825
922
  var download = (...args) => r().download(...args);
826
923
  var cookies = (...args) => r().cookies(...args);
827
924
  var send = (...args) => r().send(...args);
@@ -939,61 +1036,6 @@ async function finishLogin(ctx, input) {
939
1036
  throw new Error("Unknown auth type");
940
1037
  }
941
1038
 
942
- // src/helpers/createCookies.ts
943
- var EXPIRED2 = (/* @__PURE__ */ new Date(0)).toUTCString();
944
- var times = /(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;
945
- parse.millisecond = parse.ms = 1e-3;
946
- parse.second = parse.sec = parse.s = parse[""] = 1;
947
- parse.minute = parse.min = parse.m = parse.s * 60;
948
- parse.hour = parse.hr = parse.h = parse.m * 60;
949
- parse.day = parse.d = parse.h * 24;
950
- parse.week = parse.wk = parse.w = parse.d * 7;
951
- parse.year = parse.yr = parse.y = parse.d * 365.25;
952
- parse.month = parse.b = parse.y / 12;
953
- function parse(str) {
954
- if (str === null || str === void 0) return null;
955
- if (typeof str === "number") return str;
956
- if (typeof str !== "string") {
957
- throw new Error(`Not a string: ${str} (${typeof str})`);
958
- }
959
- str = str.toLowerCase().replace(/[,_]/g, "");
960
- const [_, value, units] = times.exec(str) || [];
961
- if (!units) return null;
962
- const unitValue = parse[units] || parse[units.replace(/s$/, "")];
963
- if (!unitValue) return null;
964
- const result = unitValue * parseFloat(value);
965
- return Math.abs(Math.round(result * 1e3));
966
- }
967
- function normalizeExpires(expires) {
968
- if (expires === null || expires === void 0) return void 0;
969
- if (expires === 0) return EXPIRED2;
970
- if (typeof expires === "string") {
971
- if (/^[\d._]+\w+$/.test(expires)) {
972
- return new Date(Date.now() + parse(expires)).toUTCString();
973
- } else {
974
- return expires;
975
- }
976
- }
977
- if (typeof expires === "number") {
978
- return new Date(Date.now() + expires).toUTCString();
979
- }
980
- if (expires instanceof Date) {
981
- return expires.toUTCString();
982
- }
983
- return void 0;
984
- }
985
- function createCookies(key, val) {
986
- if (val.value === null) val.expires = EXPIRED2;
987
- const { value, path: path2, expires, maxAge, httpOnly, secure, sameSite } = val;
988
- let str = `${key}=${value || ""};Path=${path2 || "/"}`;
989
- if (typeof expires !== "undefined") str += `;Expires=${normalizeExpires(expires)}`;
990
- if (typeof maxAge === "number") str += `;Max-Age=${maxAge}`;
991
- if (httpOnly) str += ";HttpOnly";
992
- if (secure) str += ";Secure";
993
- if (sameSite) str += `;SameSite=${sameSite}`;
994
- return str;
995
- }
996
-
997
1039
  // src/auth/state.ts
998
1040
  var NAME = "oauth_state";
999
1041
  function startState(ctx, crossSite = false) {
@@ -1606,6 +1648,7 @@ function config(options = {}) {
1606
1648
  // the added headers off; see resolveSecurity for the defaults.
1607
1649
  security: resolveSecurity(options.security)
1608
1650
  };
1651
+ if (options.cache !== void 0) settings.cache = options.cache;
1609
1652
  options.cors = options.cors || env2.CORS || null;
1610
1653
  if (options.cors) {
1611
1654
  const cors2 = {
@@ -1683,6 +1726,7 @@ function config(options = {}) {
1683
1726
  log.message("cors", origin);
1684
1727
  }
1685
1728
  if (settings.favicon) log.message("favicon", loc(settings.favicon));
1729
+ if (settings.cache !== void 0) log.message("cache", loc(options.cache));
1686
1730
  if (settings.openapi) log.message("openapi", settings.openapi.path || "/docs");
1687
1731
  return settings;
1688
1732
  }
@@ -1722,16 +1766,6 @@ function applyCors(res, ctx) {
1722
1766
  }
1723
1767
  }
1724
1768
 
1725
- // src/helpers/etag.ts
1726
- function etag(bytes) {
1727
- let h = 2166136261;
1728
- for (let i = 0; i < bytes.length; i++) {
1729
- h ^= bytes[i];
1730
- h = Math.imul(h, 16777619);
1731
- }
1732
- return `"${bytes.length.toString(16)}-${(h >>> 0).toString(16)}"`;
1733
- }
1734
-
1735
1769
  // src/helpers/createWebsocket.ts
1736
1770
  function createWebsocket(sockets, handlers) {
1737
1771
  const run = (event, socket, body) => {
@@ -1846,6 +1880,7 @@ async function parseResponse(out, ctx) {
1846
1880
  }
1847
1881
  applyCors(out, ctx);
1848
1882
  applySecurity(out, ctx);
1883
+ out = await applyCache(out, ctx);
1849
1884
  if (ctx.time?.times?.length > 1) {
1850
1885
  out.headers.set("Server-Timing", ctx.time.headers());
1851
1886
  }
@@ -3173,6 +3208,7 @@ function server(options) {
3173
3208
  export {
3174
3209
  Server,
3175
3210
  ServerError_default as ServerError,
3211
+ cache,
3176
3212
  cookies,
3177
3213
  server as default,
3178
3214
  download,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.35.1",
3
+ "version": "0.36.0",
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",