@localnerve/csp-hashes 7.0.3 → 7.0.4

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/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * CSP Hashes.
3
3
  *
4
- * Return a Vinyl transform object stream to process html files for
5
- * generating the required CSP hashes for inline and attribute scripts, styles.
4
+ * Return a Transform object stream to process Vinyl or plain file-like
5
+ * objects for generating the required CSP hashes for inline and attribute
6
+ * scripts, styles.
6
7
  *
7
8
  * Copyright (c) 2022-2025 Alex Grant (@localnerve), LocalNerve LLC
8
9
  * Licensed under the MIT license.
package/lib/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * CSP Hashes.
3
3
  *
4
- * Return a Vinyl transform object stream to process html files for
5
- * generating the required CSP hashes for inline and attribute scripts, styles.
4
+ * Return a Transform object stream to process Vinyl or plain file-like
5
+ * objects for generating the required CSP hashes for inline and attribute
6
+ * scripts, styles.
6
7
  *
7
8
  * Copyright (c) 2022-2025 Alex Grant (@localnerve), LocalNerve LLC
8
9
  * Licensed under the MIT license.
@@ -76,13 +77,13 @@ export function createCspHash(input, algo = 'sha256') {
76
77
 
77
78
  /**
78
79
  * hashstream
79
- * Accepts the processing options and returns the Vinyl transform object stream.
80
+ * Accepts the processing options and returns a Transform object stream.
80
81
  *
81
82
  * @param {Object} options
82
83
  * @param {Function} options.callback - Function to call to process the csp hashes.
83
84
  * @param {String} [options.algo] - hash algorithm, default sha256. Can be sha384, sha512.
84
85
  * @param {Boolean} [options.replace] - True if callback is used for meta html replacements, defaults to false.
85
- * @returns Transform object stream to process Vinyl objects.
86
+ * @returns Transform object stream that accepts Vinyl or plain file-like objects with `path` and `contents`.
86
87
  */
87
88
  export function hashstream ({
88
89
  algo = 'sha256',
@@ -100,10 +101,10 @@ export function hashstream ({
100
101
 
101
102
  const transformObjectStream = new Transform({
102
103
  objectMode: true,
103
- transform: (vinyl, enc, done) => {
104
- const path = vinyl.path;
105
- const content = vinyl.contents;
106
-
104
+ transform: (file, enc, done) => {
105
+ const path = file.path;
106
+ const content = file.contents;
107
+
107
108
  const hashes = {
108
109
  script: {
109
110
  elements: [],
@@ -125,12 +126,12 @@ export function hashstream ({
125
126
 
126
127
  if (replace) {
127
128
  const s = callback(path, hashes, content.toString());
128
- vinyl.contents = Buffer.from(s, enc);
129
+ file.contents = Buffer.from(s, enc);
129
130
  } else {
130
131
  callback(path, hashes);
131
132
  }
132
-
133
- done(null, vinyl);
133
+
134
+ done(null, file);
134
135
  }
135
136
  });
136
137
 
@@ -12,19 +12,19 @@ import { Transform } from 'node:stream';
12
12
  export function removeCspMeta () {
13
13
  return new Transform({
14
14
  objectMode: true,
15
- transform: (vinyl, enc, done) => {
15
+ transform: (file, enc, done) => {
16
16
  let e = null;
17
- const input = vinyl?.contents?.toString();
17
+ const input = file?.contents?.toString();
18
18
  if (input) {
19
19
  const output = input.replace(
20
20
  /("?Content-Security-Policy"?)(\s+)(content=")([^"]+)"/i, '$1$2$3$2"'
21
21
  );
22
- vinyl.contents = Buffer.from(output, enc);
22
+ file.contents = Buffer.from(output, enc);
23
23
  } else {
24
- e = new Error('removeCspMeta could not get Vinyl object file contents');
24
+ e = new Error('removeCspMeta could not get file object contents');
25
25
  e.errorCode = 'EBADINPUT';
26
26
  }
27
- done(e, vinyl);
27
+ done(e, file);
28
28
  }
29
29
  });
30
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@localnerve/csp-hashes",
3
- "version": "7.0.3",
3
+ "version": "7.0.4",
4
4
  "description": "Flexible library to generate CSP hashes",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -15,9 +15,9 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@eslint/js": "^10.0.1",
18
- "eslint": "^10.5.0",
19
- "eslint-plugin-n": "^18.1.0",
20
- "globals": "^17.6.0",
18
+ "eslint": "^10.8.0",
19
+ "eslint-plugin-n": "^18.2.2",
20
+ "globals": "^17.8.0",
21
21
  "vinyl": "^3.0.1"
22
22
  },
23
23
  "dependencies": {
package/readme.md CHANGED
@@ -22,11 +22,12 @@
22
22
  - [Build Step to Maintain CSP Headers](#build-step-to-maintain-csp-headers)
23
23
  - [Build Step to Maintain CSP Meta Tags](#build-step-to-maintain-csp-meta-tags)
24
24
  - [Build Step to Remove CSP Meta Tag Content](#build-step-to-remove-csp-meta-tag-content)
25
+ - [Native Node.js Streams (No Gulp/Vinyl Required)](#native-nodejs-streams-no-gulpvinyl-required)
25
26
  - [Non-ESM usage](#non-esm-usage)
26
27
  - [LICENSE](#license)
27
28
 
28
29
  ## Overview
29
- This Nodejs library generates script and style inline element and attribute hashes. It is for use in the generation of HTTP content security policy (CSP) headers or to replace/update Meta tags as a website build step. Ready for use with [Gulp](https://github.com/gulpjs/gulp).
30
+ This Node.js library generates script and style inline element and attribute hashes. It is for use in the generation of HTTP content security policy (CSP) headers or to replace/update Meta tags as a website build step. Works with [Gulp](https://github.com/gulpjs/gulp) via Vinyl objects, or directly with native Node.js streams using plain file-like objects.
30
31
 
31
32
  ## Breaking Changes
32
33
  + As of Version 2+, this is an ES Module. See [Non-Esm Usage](#non-esm-usage) for how to use outside of ESM.
@@ -61,7 +62,7 @@ String createCSPHash(inputString, algo = 'sha256')
61
62
  Returns a ready to use csp hash string (with quotes) in the form of `'sha256-d3ii1Pel57UO62xosCMNgTaZJhJa87Gd/X6e7UdlEU8='`.
62
63
 
63
64
  ### removeCspMeta
64
- This library also exports a convenience helper method, `removeCspMeta` that is useful for some types of development builds. This method takes no options and returns a stream that operates on [Vinyl](https://github.com/gulpjs/vinyl) objects and removes any `Content-Security-Policy` content found in the files.
65
+ This library also exports a convenience helper method, `removeCspMeta` that is useful for some types of development builds. This method takes no options and returns a stream that operates on [Vinyl](https://github.com/gulpjs/vinyl) objects or plain file-like objects with `path` and `contents` properties, removing any `Content-Security-Policy` content found in the files.
65
66
 
66
67
  ```
67
68
  Stream removeCspMeta ()
@@ -178,6 +179,70 @@ export function stripCspMetaContents (settings) {
178
179
  }
179
180
  ```
180
181
 
182
+ ### Native Node.js Streams (No Gulp/Vinyl Required)
183
+ You can use `hashstream` directly with native Node.js streams by wrapping raw file buffers into plain file-like objects. This example reads HTML files from a source directory, processes them through `hashstream`, and writes the output to a destination:
184
+
185
+ ```javascript
186
+ import fs from 'node:fs';
187
+ import path from 'node:path';
188
+ import { pipeline, Transform } from 'node:stream';
189
+ import hashstream from '@localnerve/csp-hashes';
190
+
191
+ const srcDir = './src/html';
192
+ const destDir = './dist/html';
193
+
194
+ // Wrap raw buffer chunks into plain file-like objects for the transform stream.
195
+ function wrapFileObject (filePath) {
196
+ return new Transform({
197
+ readableObjectMode: true,
198
+ construct (callback) { callback(); },
199
+ transform (chunk, enc, done) {
200
+ this.push({ path: filePath, contents: chunk });
201
+ done();
202
+ }
203
+ });
204
+ }
205
+
206
+ // Unwrap file-like objects back to raw buffers for writing.
207
+ function unwrapFileObject () {
208
+ return new Transform({
209
+ writableObjectMode: true,
210
+ construct (callback) { callback(); },
211
+ transform (fileObj, enc, done) {
212
+ this.push(fileObj.contents);
213
+ done();
214
+ }
215
+ });
216
+ }
217
+
218
+ async function processFile (filePath) {
219
+ const destPath = path.join(destDir, path.relative(srcDir, filePath));
220
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
221
+
222
+ await new Promise((resolve, reject) => {
223
+ pipeline(
224
+ fs.createReadStream(filePath),
225
+ wrapFileObject(filePath),
226
+ hashstream({
227
+ algo: 'sha256',
228
+ callback: (filePath, hashes) => {
229
+ console.log(`Processed ${path.basename(filePath)}:`);
230
+ console.log(` Script hashes: ${hashes.script.all.length}`);
231
+ console.log(` Style hashes: ${hashes.style.all.length}`);
232
+ }
233
+ }),
234
+ unwrapFileObject(),
235
+ fs.createWriteStream(destPath),
236
+ (err) => err ? reject(err) : resolve()
237
+ );
238
+ });
239
+ }
240
+
241
+ // Process all .html files in the source directory
242
+ const htmlFiles = fs.readdirSync(srcDir).filter(f => f.endsWith('.html')).map(f => path.join(srcDir, f));
243
+ await Promise.all(htmlFiles.map(processFile));
244
+ ```
245
+
181
246
  ### Non-ESM usage
182
247
  As of Version 2, this package is an ES Module, making it incompatible with `require`. To use outside of ESM, you can use this with a dynamic import as in the following example:
183
248
 
@@ -194,3 +259,7 @@ import('@localnerve/csp-hashes').then(({ hashstream }) => {
194
259
  ## LICENSE
195
260
 
196
261
  * [MIT, Alex Grant, LocalNerve, LLC](license.md)
262
+
263
+ ## Author
264
+
265
+ * Alex Grant, alex@localnerve.com, https://localnerve.com