@jsenv/core 25.2.1 → 25.3.0-alpha.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "25.2.1",
3
+ "version": "25.3.0-alpha.0",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,6 +27,9 @@
27
27
  "/src/",
28
28
  "/main.js"
29
29
  ],
30
+ "workspaces": [
31
+ "./packages/*"
32
+ ],
30
33
  "scripts": {
31
34
  "eslint": "node ./node_modules/eslint/bin/eslint.js . --ext=.js,.mjs,.cjs,.html",
32
35
  "importmap": "node ./script/importmap/importmap.js",
@@ -39,8 +42,8 @@
39
42
  "file-size": "node ./script/file_size/file_size.mjs --log",
40
43
  "prettier": "prettier --write .",
41
44
  "playwright-install": "npx playwright install-deps && npx playwright install",
42
- "packages-install": "node ./script/packages/packages_install.js",
43
45
  "certificate-install": "node ./script/dev/install_certificate_authority.mjs",
46
+ "sync-workspace-versions": "node ./script/dev/sync_workspace_versions.mjs",
44
47
  "prepublishOnly": "npm run build"
45
48
  },
46
49
  "optionalDependencies": {
@@ -59,8 +62,9 @@
59
62
  "@babel/plugin-transform-modules-systemjs": "7.16.5",
60
63
  "@c88/v8-coverage": "0.1.1",
61
64
  "@jsenv/abort": "4.1.2",
62
- "@jsenv/filesystem": "2.6.1",
65
+ "@jsenv/filesystem": "2.7.0",
63
66
  "@jsenv/importmap": "1.2.0",
67
+ "@jsenv/integrity": "0.0.1",
64
68
  "@jsenv/log": "1.5.0",
65
69
  "@jsenv/logger": "4.0.1",
66
70
  "@jsenv/server": "12.3.2",
@@ -112,12 +116,12 @@
112
116
  "@babel/plugin-transform-typescript": "7.16.1",
113
117
  "@babel/preset-env": "7.16.5",
114
118
  "@jsenv/assert": "2.4.1",
115
- "@jsenv/babel-preset": "./packages/jsenv-babel-preset",
119
+ "@jsenv/babel-preset": "1.1.2",
116
120
  "@jsenv/eslint-config": "16.0.9",
117
121
  "@jsenv/file-size-impact": "12.1.1",
118
122
  "@jsenv/github-release-package": "1.2.3",
119
123
  "@jsenv/https-local": "1.0.3",
120
- "@jsenv/importmap-eslint-resolver": "5.2.2",
124
+ "@jsenv/importmap-eslint-resolver": "5.2.5",
121
125
  "@jsenv/importmap-node-module": "5.1.0",
122
126
  "@jsenv/package-publish": "1.6.2",
123
127
  "@jsenv/performance-impact": "2.2.1",
package/readme.md CHANGED
@@ -255,6 +255,60 @@ build duration: 1.85 seconds
255
255
 
256
256
  To read more about jsenv build tool, check [jsenv build documentation](./docs/building/readme.md#jsenv-build).
257
257
 
258
+ # Installation
259
+
260
+ ```console
261
+ npm install --save-dev @jsenv/core
262
+ ```
263
+
264
+ _@jsenv/core_ is tested on Mac, Windows, Linux on Node.js 16.13.0. Other operating systems and Node.js versions are not tested.
265
+
266
+ # Configuration
267
+
268
+ We recommend to put some jsenv configuration in a top level file named _jsenv.config.mjs_.
269
+
270
+ The presence of a jsenv configuration file is **optional**.
271
+
272
+ ```js
273
+ /*
274
+ * This file exports configuration reused by other files such as
275
+ *
276
+ * script/test/test.mjs
277
+ * script/build/build.mjs
278
+ *
279
+ * Read more at https://github.com/jsenv/jsenv-core#configuration
280
+ */
281
+
282
+ export const projectDirectoryUrl = new URL("./", import.meta.url)
283
+ ```
284
+
285
+ _jsenv.config.mjs_ is meant to share configuration, other files will simply import what they need.
286
+
287
+ ```diff
288
+ import { buildProject } from '@jsenv/core'
289
+
290
+ + import { projectDirectoryUrl } from "./jsenv.config.mjs"
291
+
292
+ await buildProject({
293
+ - projectDirectoryUrl: new URL('./', import.meta.url)
294
+ + projectDirectoryUrl
295
+ })
296
+ ```
297
+
298
+ > We recommend to use ".mjs" extension when a file is written for Node.js but you can name the file as you want, "jsenv.config.js" is fine too.
299
+
300
+ # Documentation
301
+
302
+ | Link | Description |
303
+ | -------------------------------------------------------- | ------------------------------------------ |
304
+ | [Browser support](./docs/browser_support/readme.md) | Documentation around browser support |
305
+ | [Assets](./docs/assets/readme.md) | How to use assets (CSS, JSON, images, ...) |
306
+ | [Web workers](./docs/web_workers/readme.md) | How to use web workers |
307
+ | [NPM package](./docs/npm_package/readme.md) | How to use a NPM package |
308
+ | [CDN](./docs/cdn/readme.md) | How to use ressources from CDN |
309
+ | [React](./docs/react/readme.md) | How to enable react (or preact) and JSX |
310
+ | [TypeScript (Experimental)](./docs/typescript/readme.md) | How to enable TypeScript |
311
+
258
312
  # About
259
313
 
260
314
  Jsenv was first created to write tests that could be executed in different runtimes. It has naturally evolved to cover the core needs of a JavaScript project:
@@ -324,60 +378,6 @@ The logo is composed by the name at the center and two circles orbiting around i
324
378
 
325
379
  </details>
326
380
 
327
- # Installation
328
-
329
- ```console
330
- npm install --save-dev @jsenv/core
331
- ```
332
-
333
- _@jsenv/core_ is tested on Mac, Windows, Linux on Node.js 16.13.0. Other operating systems and Node.js versions are not tested.
334
-
335
- # Configuration
336
-
337
- We recommend to put some jsenv configuration in a top level file named _jsenv.config.mjs_.
338
-
339
- The presence of a jsenv configuration file is **optional**.
340
-
341
- ```js
342
- /*
343
- * This file exports configuration reused by other files such as
344
- *
345
- * script/test/test.mjs
346
- * script/build/build.mjs
347
- *
348
- * Read more at https://github.com/jsenv/jsenv-core#configuration
349
- */
350
-
351
- export const projectDirectoryUrl = new URL("./", import.meta.url)
352
- ```
353
-
354
- _jsenv.config.mjs_ is meant to share configuration, other files will simply import what they need.
355
-
356
- ```diff
357
- import { buildProject } from '@jsenv/core'
358
-
359
- + import { projectDirectoryUrl } from "./jsenv.config.mjs"
360
-
361
- await buildProject({
362
- - projectDirectoryUrl: new URL('./', import.meta.url)
363
- + projectDirectoryUrl
364
- })
365
- ```
366
-
367
- > We recommend to use ".mjs" extension when a file is written for Node.js but you can name the file as you want, "jsenv.config.js" is fine too.
368
-
369
- # Documentation
370
-
371
- | Link | Description |
372
- | -------------------------------------------------------- | ------------------------------------------ |
373
- | [Browser support](./docs/browser_support/readme.md) | Documentation around browser support |
374
- | [Assets](./docs/assets/readme.md) | How to use assets (CSS, JSON, images, ...) |
375
- | [CDN](./docs/cdn/readme.md) | How to use ressources from CDN |
376
- | [Web workers](./docs/web_workers/readme.md) | How to use web workers |
377
- | [NPM package](./docs/npm_package/readme.md) | How to use a NPM package |
378
- | [React](./docs/react/readme.md) | How to enable react (or preact) and JSX |
379
- | [TypeScript (Experimental)](./docs/typescript/readme.md) | How to enable TypeScript |
380
-
381
381
  # See also
382
382
 
383
383
  | Link | Description |
@@ -17,6 +17,8 @@ Or be sure to also reference this url somewhere in the html file like
17
17
  */
18
18
 
19
19
  import { urlToFilename, urlToRelativeUrl, resolveUrl } from "@jsenv/filesystem"
20
+ import { applyAlgoToRepresentationData } from "@jsenv/integrity"
21
+
20
22
 
21
23
  import {
22
24
  parseHtmlString,
@@ -48,7 +50,6 @@ import { collectNodesMutations } from "../parsing.utils.js"
48
50
 
49
51
  import { collectSvgMutations } from "../svg/parseSvgRessource.js"
50
52
  import { moveCssUrls } from "../css/moveCssUrls.js"
51
- import { applyAlgoToRepresentationData } from "../../integrity/integrity_algorithms.js"
52
53
 
53
54
  export const parseHtmlRessource = async (
54
55
  htmlRessource,
@@ -6,9 +6,9 @@ import {
6
6
  urlToMeta,
7
7
  writeFile,
8
8
  } from "@jsenv/filesystem"
9
+ import { validateResponseIntegrity } from "@jsenv/integrity"
9
10
 
10
11
  import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
11
- import { validateResponseIntegrity } from "@jsenv/core/src/internal/integrity/integrity_validation.js"
12
12
 
13
13
  import { originDirectoryConverter } from "./origin_directory_converter.js"
14
14
 
@@ -1,26 +0,0 @@
1
- import crypto from "node:crypto"
2
-
3
- export const isSupportedAlgorithm = (algo) => {
4
- return SUPPORTED_ALGORITHMS.includes(algo)
5
- }
6
-
7
- // https://www.w3.org/TR/SRI/#priority
8
- export const getPrioritizedHashFunction = (firstAlgo, secondAlgo) => {
9
- const firstIndex = SUPPORTED_ALGORITHMS.indexOf(firstAlgo)
10
- const secondIndex = SUPPORTED_ALGORITHMS.indexOf(secondAlgo)
11
- if (firstIndex === secondIndex) {
12
- return ""
13
- }
14
- if (firstIndex < secondIndex) {
15
- return secondAlgo
16
- }
17
- return firstAlgo
18
- }
19
-
20
- export const applyAlgoToRepresentationData = (algo, data) => {
21
- const base64Value = crypto.createHash(algo).update(data).digest("base64")
22
- return base64Value
23
- }
24
-
25
- // keep this ordered by collision resistance as it is also used by "getPrioritizedHashFunction"
26
- const SUPPORTED_ALGORITHMS = ["sha256", "sha384", "sha512"]
@@ -1,50 +0,0 @@
1
- import { isSupportedAlgorithm } from "./integrity_algorithms.js"
2
-
3
- // see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
4
- export const parseIntegrity = (string) => {
5
- const integrityMetadata = {}
6
- string
7
- .trim()
8
- .split(/\s+/)
9
- .forEach((token) => {
10
- const { isValid, algo, base64Value, optionExpression } =
11
- parseAsHashWithOptions(token)
12
- if (!isValid) {
13
- return
14
- }
15
- if (!isSupportedAlgorithm(algo)) {
16
- return
17
- }
18
- const metadataList = integrityMetadata[algo]
19
- const metadata = { base64Value, optionExpression }
20
- integrityMetadata[algo] = metadataList
21
- ? [...metadataList, metadata]
22
- : [metadata]
23
- })
24
- return integrityMetadata
25
- }
26
-
27
- // see https://w3c.github.io/webappsec-subresource-integrity/#the-integrity-attribute
28
- const parseAsHashWithOptions = (token) => {
29
- const dashIndex = token.indexOf("-")
30
- if (dashIndex === -1) {
31
- return { isValid: false }
32
- }
33
- const beforeDash = token.slice(0, dashIndex)
34
- const afterDash = token.slice(dashIndex + 1)
35
- const questionIndex = afterDash.indexOf("?")
36
- const algo = beforeDash
37
- if (questionIndex === -1) {
38
- const base64Value = afterDash
39
- const isValid = BASE64_REGEX.test(afterDash)
40
- return { isValid, algo, base64Value }
41
- }
42
- const base64Value = afterDash.slice(0, questionIndex)
43
- const optionExpression = afterDash.slice(questionIndex + 1)
44
- const isValid =
45
- BASE64_REGEX.test(afterDash) && VCHAR_REGEX.test(optionExpression)
46
- return { isValid, algo, base64Value, optionExpression }
47
- }
48
-
49
- const BASE64_REGEX = /^[A-Za-z0-9+\/=+]+$/
50
- const VCHAR_REGEX = /^[\x21-\x7E]+$/
@@ -1,23 +0,0 @@
1
- import { parseIntegrity } from "./integrity_parsing.js"
2
- import {
3
- getPrioritizedHashFunction,
4
- applyAlgoToRepresentationData,
5
- } from "./integrity_algorithms.js"
6
-
7
- export const updateIntegrity = (integrity, representationData) => {
8
- const integrityMetadata = parseIntegrity(integrity)
9
- const algos = Object.keys(integrityMetadata)
10
- if (algos.length === 0) {
11
- return ""
12
- }
13
- let strongestAlgo = algos[0]
14
- algos.slice(1).forEach((algoCandidate) => {
15
- strongestAlgo =
16
- getPrioritizedHashFunction(strongestAlgo, algoCandidate) || strongestAlgo
17
- })
18
- const base64Value = applyAlgoToRepresentationData(
19
- strongestAlgo,
20
- representationData,
21
- )
22
- return `${strongestAlgo}-${base64Value}`
23
- }
@@ -1,49 +0,0 @@
1
- import { parseIntegrity } from "./integrity_parsing.js"
2
- import {
3
- getPrioritizedHashFunction,
4
- applyAlgoToRepresentationData,
5
- } from "./integrity_algorithms.js"
6
-
7
- // https://www.w3.org/TR/SRI/#does-response-match-metadatalist
8
- export const validateResponseIntegrity = (
9
- { url, type, dataRepresentation },
10
- integrity,
11
- ) => {
12
- if (!isResponseEligibleForIntegrityValidation({ type })) {
13
- return false
14
- }
15
- const integrityMetadata = parseIntegrity(integrity)
16
- const algos = Object.keys(integrityMetadata)
17
- if (algos.length === 0) {
18
- return true
19
- }
20
- let strongestAlgo = algos[0]
21
- algos.slice(1).forEach((algoCandidate) => {
22
- strongestAlgo =
23
- getPrioritizedHashFunction(strongestAlgo, algoCandidate) || strongestAlgo
24
- })
25
- const metadataList = integrityMetadata[strongestAlgo]
26
- const actualBase64Value = applyAlgoToRepresentationData(
27
- strongestAlgo,
28
- dataRepresentation,
29
- )
30
- const acceptedBase64Values = metadataList.map(
31
- (metadata) => metadata.base64Value,
32
- )
33
- const someIsMatching = acceptedBase64Values.includes(actualBase64Value)
34
- if (someIsMatching) {
35
- return true
36
- }
37
- const error = new Error(
38
- `Integrity validation failed for ressource "${url}". The integrity found for this ressource is "${strongestAlgo}-${actualBase64Value}"`,
39
- )
40
- error.code = "EINTEGRITY"
41
- error.algorithm = strongestAlgo
42
- error.found = actualBase64Value
43
- throw error
44
- }
45
-
46
- // https://www.w3.org/TR/SRI/#is-response-eligible-for-integrity-validation
47
- const isResponseEligibleForIntegrityValidation = (response) => {
48
- return ["basic", "cors", "default"].includes(response.type)
49
- }