webpack-dev-service 0.8.0 → 0.9.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 (91) hide show
  1. package/README.md +26 -36
  2. package/client/cjs/client.cjs +14 -15
  3. package/client/cjs/events.cjs +2 -2
  4. package/client/cjs/hot.cjs +1 -1
  5. package/client/cjs/index.cjs +1 -1
  6. package/client/cjs/main.cjs +22 -41
  7. package/client/cjs/ui/overlay.cjs +7 -7
  8. package/client/cjs/ui/progress.cjs +1 -1
  9. package/client/cjs/ui/utils.cjs +1 -1
  10. package/client/esm/client.js +14 -15
  11. package/client/esm/events.js +2 -2
  12. package/client/esm/hot.js +1 -1
  13. package/client/esm/index.js +1 -1
  14. package/client/esm/main.js +22 -41
  15. package/client/esm/ui/overlay.js +7 -7
  16. package/client/esm/ui/progress.js +1 -1
  17. package/client/esm/ui/utils.js +1 -1
  18. package/package.json +12 -8
  19. package/server/cjs/{dev/utils/compose.cjs → compose.cjs} +8 -10
  20. package/server/cjs/dev/Service.cjs +269 -0
  21. package/server/cjs/dev/index.cjs +9 -11
  22. package/server/cjs/dev/middleware.cjs +54 -28
  23. package/server/cjs/dev/utils/fs.cjs +29 -0
  24. package/server/cjs/dev/utils/{boundary.cjs → hash.cjs} +14 -10
  25. package/server/cjs/dev/utils/http.cjs +168 -10
  26. package/server/cjs/dev/utils/path.cjs +46 -0
  27. package/server/cjs/dev/utils/paths.cjs +59 -0
  28. package/server/cjs/dev/utils/ready.cjs +6 -9
  29. package/server/cjs/dev/utils/setupHooks.cjs +51 -40
  30. package/server/cjs/dev/utils/setupOutputFileSystem.cjs +6 -39
  31. package/server/cjs/dev/utils/setupWatching.cjs +3 -3
  32. package/server/cjs/dev/utils/setupWriteToDisk.cjs +23 -31
  33. package/server/cjs/hot/Socket.cjs +88 -111
  34. package/server/cjs/hot/index.cjs +13 -15
  35. package/server/cjs/hot/utils.cjs +100 -0
  36. package/server/cjs/index.cjs +17 -7
  37. package/server/cjs/schema.cjs +127 -0
  38. package/server/cjs/utils.cjs +45 -0
  39. package/server/esm/{dev/utils/compose.js → compose.js} +8 -10
  40. package/server/esm/dev/Service.js +260 -0
  41. package/server/esm/dev/index.js +8 -10
  42. package/server/esm/dev/middleware.js +53 -27
  43. package/server/esm/dev/utils/fs.js +27 -0
  44. package/server/esm/dev/utils/{boundary.js → hash.js} +14 -10
  45. package/server/esm/dev/utils/http.js +159 -8
  46. package/server/esm/dev/utils/path.js +42 -0
  47. package/server/esm/dev/utils/paths.js +57 -0
  48. package/server/esm/dev/utils/ready.js +6 -9
  49. package/server/esm/dev/utils/setupHooks.js +46 -35
  50. package/server/esm/dev/utils/setupOutputFileSystem.js +5 -38
  51. package/server/esm/dev/utils/setupWatching.js +2 -2
  52. package/server/esm/dev/utils/setupWriteToDisk.js +22 -30
  53. package/server/esm/hot/Socket.js +87 -110
  54. package/server/esm/hot/index.js +13 -15
  55. package/server/esm/hot/utils.js +93 -0
  56. package/server/esm/index.js +17 -7
  57. package/server/esm/schema.js +125 -0
  58. package/server/esm/utils.js +37 -0
  59. package/types/client/client.d.ts +1 -1
  60. package/types/client/events.d.ts +6 -6
  61. package/types/client/message.d.ts +6 -5
  62. package/types/client/ui/overlay.d.ts +1 -1
  63. package/types/server/dev/Service.d.ts +82 -0
  64. package/types/server/dev/index.d.ts +4 -4
  65. package/types/server/dev/interface.d.ts +26 -34
  66. package/types/server/dev/utils/fs.d.ts +27 -0
  67. package/types/server/dev/utils/hash.d.ts +9 -0
  68. package/types/server/dev/utils/http.d.ts +31 -14
  69. package/types/server/dev/utils/path.d.ts +22 -0
  70. package/types/server/dev/utils/paths.d.ts +7 -0
  71. package/types/server/dev/utils/ready.d.ts +2 -2
  72. package/types/server/hot/Socket.d.ts +6 -10
  73. package/types/server/hot/index.d.ts +4 -9
  74. package/types/server/hot/interface.d.ts +20 -0
  75. package/types/server/hot/utils.d.ts +13 -0
  76. package/types/server/index.d.ts +8 -8
  77. package/types/server/interface.d.ts +9 -0
  78. package/types/server/schema.d.ts +7 -0
  79. package/types/server/utils.d.ts +12 -0
  80. package/global.d.ts +0 -16
  81. package/server/cjs/dev/Files.cjs +0 -394
  82. package/server/cjs/dev/utils/common.cjs +0 -98
  83. package/server/cjs/dev/utils/getPaths.cjs +0 -65
  84. package/server/esm/dev/Files.js +0 -384
  85. package/server/esm/dev/utils/common.js +0 -98
  86. package/server/esm/dev/utils/getPaths.js +0 -63
  87. package/types/server/dev/Files.d.ts +0 -83
  88. package/types/server/dev/utils/boundary.d.ts +0 -8
  89. package/types/server/dev/utils/common.d.ts +0 -44
  90. package/types/server/dev/utils/getPaths.d.ts +0 -10
  91. /package/types/server/{dev/utils/compose.d.ts → compose.d.ts} +0 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @package webpack-dev-service
3
+ * @license MIT
4
+ * @version 0.9.0
5
+ * @author nuintun <nuintun@qq.com>
6
+ * @description A koa 2 middleware for webpack development and hot reloading.
7
+ * @see https://github.com/nuintun/webpack-dev-service#readme
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ /**
13
+ * @module utils
14
+ */
15
+ const { toString } = Object.prototype;
16
+ const PLUGIN_NAME = 'webpack-dev-service';
17
+ function isObject(value) {
18
+ return toString.call(value) === '[object Object]';
19
+ }
20
+ function isString(value) {
21
+ return toString.call(value) === '[object String]';
22
+ }
23
+ function isBoolean(value) {
24
+ return toString.call(value) === '[object Boolean]';
25
+ }
26
+ function isFunction(value) {
27
+ return typeof value === 'function';
28
+ }
29
+ function getCompilers(compiler) {
30
+ if (isMultiCompilerMode(compiler)) {
31
+ return compiler.compilers;
32
+ }
33
+ return [compiler];
34
+ }
35
+ function isMultiCompilerMode(compiler) {
36
+ return 'compilers' in compiler;
37
+ }
38
+
39
+ exports.PLUGIN_NAME = PLUGIN_NAME;
40
+ exports.getCompilers = getCompilers;
41
+ exports.isBoolean = isBoolean;
42
+ exports.isFunction = isFunction;
43
+ exports.isMultiCompilerMode = isMultiCompilerMode;
44
+ exports.isObject = isObject;
45
+ exports.isString = isString;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.8.0
4
+ * @version 0.9.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -24,15 +24,13 @@ async function dispatch(middlewares, index, stack, context, next) {
24
24
  }
25
25
  stack.index = index;
26
26
  const { length } = middlewares;
27
- if (index <= length) {
28
- if (index < length) {
29
- const middleware = middlewares[index];
30
- await middleware(context, () => {
31
- return dispatch(middlewares, index + 1, stack, context, next);
32
- });
33
- } else if (next) {
34
- await next();
35
- }
27
+ if (index < length) {
28
+ const middleware = middlewares[index];
29
+ await middleware(context, () => {
30
+ return dispatch(middlewares, index + 1, stack, context, next);
31
+ });
32
+ } else if (next) {
33
+ await next();
36
34
  }
37
35
  }
38
36
  /**
@@ -0,0 +1,260 @@
1
+ /**
2
+ * @package webpack-dev-service
3
+ * @license MIT
4
+ * @version 0.9.0
5
+ * @author nuintun <nuintun@qq.com>
6
+ * @description A koa 2 middleware for webpack development and hot reloading.
7
+ * @see https://github.com/nuintun/webpack-dev-service#readme
8
+ */
9
+
10
+ import createEtag from 'etag';
11
+ import destroy from 'destroy';
12
+ import { PassThrough } from 'stream';
13
+ import { isFunction } from '../utils.js';
14
+ import { stat } from './utils/fs.js';
15
+ import { resolve, extname, join } from 'path';
16
+ import { unixify, isOutRoot, hasTrailingSlash } from './utils/path.js';
17
+ import { isConditionalGET, isPreconditionFailure, parseRanges } from './utils/http.js';
18
+
19
+ /**
20
+ * @module Service
21
+ */
22
+ /**
23
+ * @class Service
24
+ */
25
+ class Service {
26
+ /**
27
+ * @constructor
28
+ * @description Create file service.
29
+ * @param root File service root.
30
+ * @param options File service options.
31
+ */
32
+ constructor(root, options) {
33
+ this.options = options;
34
+ this.root = unixify(resolve(root));
35
+ }
36
+ /**
37
+ * @private
38
+ * @method isIgnore
39
+ * @description Check if path is ignore.
40
+ * @param path File path.
41
+ */
42
+ isIgnore(path) {
43
+ const { ignore } = this.options;
44
+ return (isFunction(ignore) ? ignore(path) : false) === true;
45
+ }
46
+ /**
47
+ * @private
48
+ * @method setupHeaders
49
+ * @description Setup headers
50
+ * @param context Koa context
51
+ * @param path File path
52
+ * @param stats File stats
53
+ */
54
+ setupHeaders(context, path, stats) {
55
+ const { options } = this;
56
+ const { headers, etag } = options;
57
+ // Set status.
58
+ context.status = 200;
59
+ // Set Content-Type.
60
+ context.type = extname(path);
61
+ // Accept-Ranges.
62
+ if (options.acceptRanges === false) {
63
+ // Set Accept-Ranges to none tell client not support.
64
+ context.set('Accept-Ranges', 'none');
65
+ } else {
66
+ // Set Accept-Ranges.
67
+ context.set('Accept-Ranges', 'bytes');
68
+ }
69
+ // ETag.
70
+ if (etag === false) {
71
+ // Remove ETag.
72
+ context.remove('ETag');
73
+ } else {
74
+ // Set ETag.
75
+ context.set('ETag', createEtag(stats));
76
+ }
77
+ // Last-Modified.
78
+ if (options.lastModified === false) {
79
+ // Remove Last-Modified.
80
+ context.remove('Last-Modified');
81
+ } else {
82
+ // Set mtime utc string.
83
+ context.set('Last-Modified', stats.mtime.toUTCString());
84
+ }
85
+ // Set headers.
86
+ if (headers) {
87
+ if (isFunction(headers)) {
88
+ const fields = headers(path, stats);
89
+ if (fields) {
90
+ context.set(fields);
91
+ }
92
+ } else {
93
+ context.set(headers);
94
+ }
95
+ }
96
+ }
97
+ /**
98
+ * @private
99
+ * @method write
100
+ * @description Write file to stream.
101
+ * @param stream Destination stream.
102
+ * @param path The file path to read.
103
+ * @param range The range to read.
104
+ * @param end Is destory destination stream after read complete.
105
+ */
106
+ write(stream, path, range, end) {
107
+ const { fs } = this.options;
108
+ return new Promise(resolve => {
109
+ // Range prefix and suffix.
110
+ const { prefix, suffix } = range;
111
+ // Create file stream reader.
112
+ const reader = fs.createReadStream(path, range);
113
+ // File read stream open.
114
+ if (prefix) {
115
+ reader.once('open', () => {
116
+ // Write prefix boundary.
117
+ stream.write(prefix);
118
+ });
119
+ }
120
+ // File read stream end.
121
+ if (suffix) {
122
+ reader.once('end', () => {
123
+ // Push suffix boundary.
124
+ stream.write(suffix);
125
+ });
126
+ }
127
+ // File read stream error.
128
+ reader.once('error', () => {
129
+ // End stream.
130
+ stream.end();
131
+ // Unpipe.
132
+ reader.unpipe();
133
+ // Destroy.
134
+ destroy(reader);
135
+ // Resolve.
136
+ resolve(false);
137
+ });
138
+ // File read stream close.
139
+ reader.once('close', () => {
140
+ // Unpipe.
141
+ reader.unpipe();
142
+ // Destroy.
143
+ destroy(reader);
144
+ // Resolve.
145
+ resolve(true);
146
+ });
147
+ // Write data to buffer.
148
+ reader.pipe(stream, { end });
149
+ });
150
+ }
151
+ /**
152
+ * @private
153
+ * @method send
154
+ * @description Send file.
155
+ * @param context Koa context.
156
+ * @param path File path.
157
+ * @param ranges Read ranges.
158
+ */
159
+ async send(context, path, ranges) {
160
+ // Set stream body, highWaterMark 64kb.
161
+ const stream = new PassThrough({
162
+ highWaterMark: 65536
163
+ });
164
+ // Set response body.
165
+ context.body = stream;
166
+ // Ranges length.
167
+ let { length } = ranges;
168
+ // Write ranges to stream.
169
+ for (const range of ranges) {
170
+ // Write range.
171
+ const passed = await this.write(stream, path, range, --length === 0);
172
+ // If not passed, break.
173
+ if (!passed) {
174
+ break;
175
+ }
176
+ }
177
+ }
178
+ /**
179
+ * @public
180
+ * @method response
181
+ * @description Response to koa context.
182
+ * @param context Koa context.
183
+ */
184
+ async response(context) {
185
+ const { root } = this;
186
+ // Only support GET and HEAD (405).
187
+ if (context.method !== 'GET' && context.method !== 'HEAD') {
188
+ return false;
189
+ }
190
+ // Get path of file.
191
+ const path = unixify(join(root, context.path));
192
+ // Malicious path (403).
193
+ if (isOutRoot(path, root)) {
194
+ return false;
195
+ }
196
+ // Is ignore path or file (403).
197
+ if (this.isIgnore(path)) {
198
+ return false;
199
+ }
200
+ // File stats.
201
+ const stats = await stat(this.options.fs, path);
202
+ // Check file stats.
203
+ if (
204
+ // File not exist (404 | 500).
205
+ stats == null ||
206
+ // Is directory (403).
207
+ stats.isDirectory() ||
208
+ // Not a directory but has trailing slash (404).
209
+ hasTrailingSlash(path)
210
+ ) {
211
+ return false;
212
+ }
213
+ // Setup headers.
214
+ this.setupHeaders(context, path, stats);
215
+ // Conditional get support.
216
+ if (isConditionalGET(context)) {
217
+ // Request precondition failure.
218
+ if (isPreconditionFailure(context)) {
219
+ return context.throw(412);
220
+ }
221
+ // Request fresh (304).
222
+ if (context.fresh) {
223
+ // Set status.
224
+ context.status = 304;
225
+ // Set body null.
226
+ context.body = null;
227
+ // File found.
228
+ return true;
229
+ }
230
+ }
231
+ // Head request.
232
+ if (context.method === 'HEAD') {
233
+ // Set Content-Length.
234
+ context.length = stats.size;
235
+ // Set body null
236
+ context.body = null;
237
+ // File found.
238
+ return true;
239
+ }
240
+ // Parsed ranges.
241
+ const ranges = parseRanges(context, stats);
242
+ // 416
243
+ if (ranges === -1) {
244
+ // Set Content-Range.
245
+ context.set('Content-Range', `bytes */${stats.size}`);
246
+ // Unsatisfiable 416.
247
+ return context.throw(416);
248
+ }
249
+ // 400.
250
+ if (ranges === -2) {
251
+ return context.throw(400);
252
+ }
253
+ // Send file.
254
+ this.send(context, path, ranges);
255
+ // File found.
256
+ return true;
257
+ }
258
+ }
259
+
260
+ export { Service };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.8.0
4
+ * @version 0.9.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { ready } from './utils/ready.js';
11
11
  import { middleware } from './middleware.js';
12
- import { PLUGIN_NAME } from './utils/common.js';
12
+ import { PLUGIN_NAME } from '../utils.js';
13
13
  import { setupHooks } from './utils/setupHooks.js';
14
14
  import { setupWatching } from './utils/setupWatching.js';
15
15
  import { setupWriteToDisk } from './utils/setupWriteToDisk.js';
@@ -23,7 +23,6 @@ function setup(compiler, options) {
23
23
  options,
24
24
  compiler,
25
25
  stats: null,
26
- state: false,
27
26
  callbacks: [],
28
27
  logger: compiler.getInfrastructureLogger(PLUGIN_NAME)
29
28
  };
@@ -35,11 +34,11 @@ function setup(compiler, options) {
35
34
  setupWatching(context);
36
35
  return context;
37
36
  }
38
- function dev(compiler, options = {}) {
37
+ function dev(compiler, options) {
39
38
  const context = setup(compiler, options);
40
39
  return Object.assign(middleware(context), {
41
- isReady() {
42
- return context.state;
40
+ get state() {
41
+ return !!context.stats;
43
42
  },
44
43
  get logger() {
45
44
  return context.logger;
@@ -47,12 +46,11 @@ function dev(compiler, options = {}) {
47
46
  ready(callback) {
48
47
  ready(context, callback);
49
48
  },
50
- invalidate(callback) {
51
- ready(context, callback);
52
- context.watching.invalidate();
53
- },
54
49
  close(callback) {
55
50
  context.watching.close(callback);
51
+ },
52
+ invalidate(callback) {
53
+ context.watching.invalidate(callback);
56
54
  }
57
55
  });
58
56
  }
@@ -1,57 +1,82 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.8.0
4
+ * @version 0.9.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
8
8
  */
9
9
 
10
- import Files from './Files.js';
11
- import { decodeURI } from './utils/common.js';
12
- import { getPaths } from './utils/getPaths.js';
10
+ import { Service } from './Service.js';
11
+ import { ready } from './utils/ready.js';
12
+ import { decodeURI } from './utils/http.js';
13
+ import { getPaths } from './utils/paths.js';
13
14
 
14
15
  /**
15
16
  * @module middleware
16
17
  */
17
- const cache = new WeakMap();
18
- async function getFilesInstances(context, name) {
19
- const cached = cache.get(context.compiler);
20
- if (cached != null) {
18
+ function getFileServices(context, stats) {
19
+ const cached = context.services;
20
+ // Cache hit.
21
+ if (cached) {
21
22
  return cached;
22
23
  }
23
- const { options } = context;
24
- const instances = [];
25
- const paths = await getPaths(context, name);
26
- for (const { outputPath, publicPath } of paths) {
27
- instances.push({
24
+ const paths = getPaths(stats);
25
+ const { fs, options } = context;
26
+ const services = [];
27
+ const { etag, ignore, headers, acceptRanges, lastModified } = options;
28
+ // Get the file services.
29
+ for (const [outputPath, publicPath] of paths) {
30
+ services.push([
28
31
  publicPath,
29
- files: new Files(outputPath, {
30
- etag: options.etag,
31
- headers: options.headers,
32
- fs: context.outputFileSystem,
33
- acceptRanges: options.acceptRanges,
34
- cacheControl: options.cacheControl,
35
- lastModified: options.lastModified
32
+ new Service(outputPath, {
33
+ fs,
34
+ etag,
35
+ ignore,
36
+ headers,
37
+ acceptRanges,
38
+ lastModified
36
39
  })
37
- });
40
+ ]);
38
41
  }
39
- return instances;
42
+ // Cache services.
43
+ context.services = services;
44
+ // Return services.
45
+ return services;
46
+ }
47
+ function getFileServicesAsync(context, path) {
48
+ return new Promise(resolve => {
49
+ const { stats } = context;
50
+ // If stats exists, resolve immediately.
51
+ if (stats) {
52
+ resolve(getFileServices(context, stats));
53
+ } else {
54
+ // Log waiting info.
55
+ context.logger.info(`wait until bundle finished: ${path}`);
56
+ // Otherwise, wait until bundle finished.
57
+ ready(context, stats => {
58
+ resolve(getFileServices(context, stats));
59
+ });
60
+ }
61
+ });
40
62
  }
41
63
  function middleware(context) {
64
+ // Middleware.
42
65
  return async (ctx, next) => {
43
66
  const path = decodeURI(ctx.path);
44
- // Path -1 or null byte(s)
67
+ // Path -1 or null byte(s).
45
68
  if (path === -1 || path.includes('\0')) {
46
69
  return ctx.throw(400);
47
70
  }
48
- ctx.path = path;
71
+ // Is path respond.
49
72
  let respond = false;
50
- const instances = await getFilesInstances(context, path);
51
- for (const { files, publicPath } of instances) {
73
+ // Get the file services.
74
+ const services = await getFileServicesAsync(context, path);
75
+ // Try to respond.
76
+ for (const [publicPath, service] of services) {
52
77
  if (path.startsWith(publicPath)) {
53
78
  ctx.path = path.slice(publicPath.length);
54
- respond = await files.response(ctx);
79
+ respond = await service.response(ctx);
55
80
  if (respond) {
56
81
  return;
57
82
  } else {
@@ -59,6 +84,7 @@ function middleware(context) {
59
84
  }
60
85
  }
61
86
  }
87
+ // Not respond.
62
88
  if (!respond) {
63
89
  await next();
64
90
  }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @package webpack-dev-service
3
+ * @license MIT
4
+ * @version 0.9.0
5
+ * @author nuintun <nuintun@qq.com>
6
+ * @description A koa 2 middleware for webpack development and hot reloading.
7
+ * @see https://github.com/nuintun/webpack-dev-service#readme
8
+ */
9
+
10
+ /**
11
+ * @module fs
12
+ */
13
+ /**
14
+ * @function stat
15
+ * @description Get file stats.
16
+ * @param fs The file system to used.
17
+ * @param path The file path.
18
+ */
19
+ function stat(fs, path) {
20
+ return new Promise(resolve => {
21
+ fs.stat(path, (error, stats) => {
22
+ resolve(error ? null : stats);
23
+ });
24
+ });
25
+ }
26
+
27
+ export { stat };
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * @package webpack-dev-service
3
3
  * @license MIT
4
- * @version 0.8.0
4
+ * @version 0.9.0
5
5
  * @author nuintun <nuintun@qq.com>
6
6
  * @description A koa 2 middleware for webpack development and hot reloading.
7
7
  * @see https://github.com/nuintun/webpack-dev-service#readme
8
8
  */
9
9
 
10
+ import { getRandomValues } from 'crypto';
11
+
10
12
  /**
11
- * @module boundary
13
+ * @module hash
12
14
  */
13
15
  // prettier-ignore
14
16
  const CHARS = [
@@ -25,16 +27,18 @@ const CHARS = [
25
27
  ];
26
28
  /**
27
29
  * @function generate
28
- * @description Generate a boundary.
30
+ * @description Generate a hash.
31
+ * @param length The length of hash.
29
32
  */
30
- function generate() {
31
- let boundary = '';
32
- // Create boundary.
33
- for (let i = 0; i < 38; i++) {
34
- boundary += CHARS[Math.floor(Math.random() * 62)];
33
+ function generate(length = 32) {
34
+ let hash = '';
35
+ const randomValues = getRandomValues(new Uint8Array(length));
36
+ // Create hash.
37
+ for (const value of randomValues) {
38
+ hash += CHARS[value % 62];
35
39
  }
36
- // Return boundary.
37
- return boundary;
40
+ // Return hash.
41
+ return hash;
38
42
  }
39
43
 
40
44
  export { generate };