@spirobel/mininext 0.3.2 → 0.3.3

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/dist/mininext.js CHANGED
@@ -11,7 +11,19 @@ async function build(backendPath = "backend/backend.ts") {
11
11
  await devServer();
12
12
  }
13
13
  }
14
- const myPlugin = {
14
+ const streamPlugin = {
15
+ name: "node stream in the frontend",
16
+ setup(build) {
17
+ build.onResolve({ filter: /^stream$/ }, (args) => {
18
+ const path_to_crypto_lib = path.resolve(projectRoot(), "node_modules/stream-browserify/index.js");
19
+ if (path_to_crypto_lib)
20
+ return {
21
+ path: path_to_crypto_lib,
22
+ };
23
+ });
24
+ },
25
+ };
26
+ const bufferPlugin = {
15
27
  name: "node buffer in the frontend",
16
28
  setup(build) {
17
29
  build.onResolve({ filter: /^buffer$/ }, (args) => {
@@ -23,6 +35,18 @@ const myPlugin = {
23
35
  });
24
36
  },
25
37
  };
38
+ const cryptoPlugin = {
39
+ name: "node crypto in the frontend",
40
+ setup(build) {
41
+ build.onResolve({ filter: /^crypto$/ }, (args) => {
42
+ const path_to_crypto_lib = path.resolve(projectRoot(), "node_modules/crypto-browserify/index.js");
43
+ if (path_to_crypto_lib)
44
+ return {
45
+ path: path_to_crypto_lib,
46
+ };
47
+ });
48
+ },
49
+ };
26
50
  async function buildBackend(backendPath = "backend/backend.ts") {
27
51
  global.FrontendScriptUrls = [];
28
52
  global.FrontendScripts = [];
@@ -63,7 +87,7 @@ async function buildFrontend(file) {
63
87
  naming: "[name]-[hash].[ext]",
64
88
  minify: Bun.argv[2] === "dev" ? false : true, //production
65
89
  target: "browser",
66
- plugins: [myPlugin],
90
+ plugins: [bufferPlugin, streamPlugin, cryptoPlugin],
67
91
  });
68
92
  if (!result?.outputs[0]?.path)
69
93
  console.log(result);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ import { expect, test, mock } from "bun:test";
2
+ import { html, url } from "../mininext/mininext";
3
+ // Example of creating a mock request object
4
+ const mockRequestObject = {
5
+ method: "GET", // or 'POST', etc.
6
+ url: "http://example.com/api/some-endpoint",
7
+ body: JSON.stringify({ key: "value" }),
8
+ headers: {
9
+ "Content-Type": "application/json",
10
+ get: () => undefined,
11
+ },
12
+ };
13
+ const makeMockRequest = mock(() => mockRequestObject);
14
+ test("no xss when BasedHtml inside of html", async () => {
15
+ const req = makeMockRequest();
16
+ url.set([
17
+ [
18
+ "/",
19
+ (mini) => {
20
+ const basedHtmlString = html `<h2>
21
+ this html string is resolved (it can't contain functions like
22
+ mini.html HtmlStrings)
23
+ </h2>
24
+ ${"<script>alert(1)</script>"}`;
25
+ return mini.html `<h1> ${"<script>alert(1)</script>"}this HtmlString can contain functions,
26
+ that get resolved at request time.</h1>${basedHtmlString}
27
+ <h3>${(mini) => mini.html ` ${"<script>alert(1)</script>"}it gives you convenient access to the request object,
28
+ anywhere in your code base:${mini.req.url}.
29
+ no need to do "props drilling" anymore. just write a function like this:
30
+ (mini: Mini)=> return some html and you are golden. ${"<script>alert(1)</script>"} `}`;
31
+ },
32
+ ],
33
+ ]);
34
+ const response = await url.match(req, "/");
35
+ const responseText = await response?.text();
36
+ expect(responseText).not.toInclude("<script>alert(1)</script>");
37
+ expect(responseText).toInclude("&lt;script&gt;alert(1)&lt;/script&gt;");
38
+ });
package/mininext/html.ts CHANGED
@@ -7,6 +7,9 @@ import type {
7
7
  export type HtmlStringValues<T = unknown> =
8
8
  | HtmlString
9
9
  | HtmlString[]
10
+ | BasedHtml
11
+ | BasedHtml[]
12
+ | (BasedHtml | HtmlString)[]
10
13
  | string
11
14
  | number
12
15
  | HtmlHandler<T>
@@ -36,8 +39,11 @@ export class HtmlString extends Array {
36
39
  }
37
40
  } else if (typeof htmlPiece === "function") {
38
41
  let resolvedHtmlPiece = await htmlPiece(mini); //passing mini
42
+ //same cases as outer if statement
39
43
  if (resolvedHtmlPiece instanceof HtmlString) {
40
44
  resolvedHtmlPiece = await resolvedHtmlPiece.resolve(mini);
45
+ } else if (htmlPiece instanceof BasedHtml) {
46
+ this[index] = htmlPiece;
41
47
  } else {
42
48
  if (this instanceof JsonString || this instanceof DangerJsonInHtml) {
43
49
  resolvedHtmlPiece = JSON.stringify(resolvedHtmlPiece);
@@ -49,6 +55,8 @@ export class HtmlString extends Array {
49
55
  }
50
56
  // Replace the function with the resolved HTML piece in place
51
57
  this[index] = resolvedHtmlPiece;
58
+ } else if (htmlPiece instanceof BasedHtml) {
59
+ this[index] = htmlPiece;
52
60
  }
53
61
  }
54
62
  this.resolved = true;
@@ -79,7 +87,9 @@ export function html<X = unknown>(
79
87
  // we can pass arrays of HtmlString and they will get flattened in the HtmlResponder
80
88
  if (
81
89
  Array.isArray(value) &&
82
- value.every((val) => val instanceof HtmlString)
90
+ value.every(
91
+ (val) => val instanceof HtmlString || val instanceof BasedHtml
92
+ )
83
93
  ) {
84
94
  // If the value is an array of HtmlString objects, add the whole array as a single value
85
95
  const notResolved = new HtmlString(...(value as any[]));
@@ -95,7 +105,7 @@ export function html<X = unknown>(
95
105
  to pass through a html template function to get escaped. You can do
96
106
  html -> dangerjson -> html if you want!
97
107
  </div>`;
98
- } else if (!(value instanceof HtmlString)) {
108
+ } else if (!(value instanceof HtmlString || value instanceof BasedHtml)) {
99
109
  const notEmpty = value || "";
100
110
  // values will be escaped by default
101
111
  values[index] = Bun.escapeHTML(notEmpty + "");
@@ -144,7 +154,9 @@ function JsonTemplateProcessor(danger: boolean = false) {
144
154
  // we can pass arrays of HtmlString and they will get flattened in the HtmlResponder
145
155
  if (
146
156
  Array.isArray(value) &&
147
- value.every((val) => val instanceof HtmlString)
157
+ value.every(
158
+ (val) => val instanceof HtmlString || val instanceof BasedHtml
159
+ )
148
160
  ) {
149
161
  // If the value is an array of HtmlString objects, add the whole array as a single value
150
162
  const notResolved = new HtmlString(...(value as any[]));
@@ -154,8 +166,8 @@ function JsonTemplateProcessor(danger: boolean = false) {
154
166
  } else if (typeof value === "function") {
155
167
  jsonStringArray.resolved = false;
156
168
  values[index] = value;
157
- } else if (value instanceof HtmlString) {
158
- if (!value.resolved) {
169
+ } else if (value instanceof HtmlString || value instanceof BasedHtml) {
170
+ if (value instanceof HtmlString && !value.resolved) {
159
171
  jsonStringArray.resolved = false;
160
172
  }
161
173
  values[index] = value;
@@ -265,11 +277,11 @@ export async function htmlResponder(
265
277
  }
266
278
  const definitelyResolved = await maybeUnresolved.resolve(mini);
267
279
  const flattend = definitelyResolved.flat(Infinity);
268
-
269
280
  async function* stepGen() {
270
281
  let index = 0;
271
282
  while (index < flattend.length) {
272
- yield flattend[index++];
283
+ const step = flattend[index++];
284
+ if (step) yield String(step);
273
285
  }
274
286
  }
275
287
  function Stream(a: any) {
@@ -24,7 +24,22 @@ async function build(backendPath: string = "backend/backend.ts") {
24
24
  }
25
25
  }
26
26
 
27
- const myPlugin: BunPlugin = {
27
+ const streamPlugin: BunPlugin = {
28
+ name: "node stream in the frontend",
29
+ setup(build) {
30
+ build.onResolve({ filter: /^stream$/ }, (args) => {
31
+ const path_to_stream_lib = path.resolve(
32
+ projectRoot(),
33
+ "node_modules/stream-browserify/index.js"
34
+ );
35
+ if (path_to_stream_lib)
36
+ return {
37
+ path: path_to_stream_lib,
38
+ };
39
+ });
40
+ },
41
+ };
42
+ const bufferPlugin: BunPlugin = {
28
43
  name: "node buffer in the frontend",
29
44
  setup(build) {
30
45
  build.onResolve({ filter: /^buffer$/ }, (args) => {
@@ -39,6 +54,21 @@ const myPlugin: BunPlugin = {
39
54
  });
40
55
  },
41
56
  };
57
+ const cryptoPlugin: BunPlugin = {
58
+ name: "node crypto in the frontend",
59
+ setup(build) {
60
+ build.onResolve({ filter: /^crypto$/ }, (args) => {
61
+ const path_to_crypto_lib = path.resolve(
62
+ projectRoot(),
63
+ "node_modules/crypto-browserify/index.js"
64
+ );
65
+ if (path_to_crypto_lib)
66
+ return {
67
+ path: path_to_crypto_lib,
68
+ };
69
+ });
70
+ },
71
+ };
42
72
  async function buildBackend(backendPath: string = "backend/backend.ts") {
43
73
  global.FrontendScriptUrls = [];
44
74
  global.FrontendScripts = [];
@@ -83,7 +113,7 @@ async function buildFrontend(file: string) {
83
113
  naming: "[name]-[hash].[ext]",
84
114
  minify: Bun.argv[2] === "dev" ? false : true, //production
85
115
  target: "browser",
86
- plugins: [myPlugin],
116
+ plugins: [bufferPlugin, streamPlugin, cryptoPlugin],
87
117
  });
88
118
  if (!result?.outputs[0]?.path) console.log(result);
89
119
  const url = path.basename(result.outputs[0].path);
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  },
14
14
  "files": ["dist", "mininext"],
15
- "version": "0.3.2",
15
+ "version": "0.3.3",
16
16
  "devDependencies": {
17
17
  "@types/bun": "latest"
18
18
  },