From 03758c48ee6deaa1a2611f3c75017807ed8bd643 Mon Sep 17 00:00:00 2001 From: Erin Date: Fri, 28 Feb 2025 09:35:44 -0500 Subject: [PATCH 1/3] separate node and browser implementations of blob compression functions --- packages/toolbox-storage/package.json | 12 +++++-- .../src/usernotes/RawUsernotes.test.ts | 4 --- .../src/usernotes/RawUsernotes.ts | 16 ++++------ .../usernotes/blobInternals.default.test.ts | 5 +++ .../src/usernotes/blobInternals.default.ts | 32 +++++++++++++++++++ .../src/usernotes/blobInternals.node.test.ts | 5 +++ .../src/usernotes/blobInternals.node.ts | 15 +++++++++ 7 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 packages/toolbox-storage/src/usernotes/blobInternals.default.test.ts create mode 100644 packages/toolbox-storage/src/usernotes/blobInternals.default.ts create mode 100644 packages/toolbox-storage/src/usernotes/blobInternals.node.test.ts create mode 100644 packages/toolbox-storage/src/usernotes/blobInternals.node.ts diff --git a/packages/toolbox-storage/package.json b/packages/toolbox-storage/package.json index 439498d..292b961 100644 --- a/packages/toolbox-storage/package.json +++ b/packages/toolbox-storage/package.json @@ -8,6 +8,12 @@ "files": [ "dist/*" ], + "imports": { + "#blobInternals": { + "node": "./dist/usernotes/blobInternals.node.js", + "default": "./dist/usernotes/blobInternals.default.js" + } + }, "scripts": { "fmt": "dprint fmt", "fmt:check": "dprint check", @@ -25,13 +31,15 @@ "url": "https://github.com/toolbox-team/storage/issues" }, "homepage": "https://github.com/toolbox-team/storage/tree/main/packages/toolbox-storage#readme", - "dependencies": { + "optionalDependencies": { + "core-js": "^3.40.0", "pako": "^1.0.11" }, "devDependencies": { "@ava/typescript": "^5.0.0", "@tsconfig/node-lts": "^22.0.1", - "@types/node": "^13.7.4", + "@types/node": "^22.13.5", + "@types/pako": "^2.0.3", "ava": "^6.2.0", "dprint": "^0.40.2", "nyc": "^15.0.0" diff --git a/packages/toolbox-storage/src/usernotes/RawUsernotes.test.ts b/packages/toolbox-storage/src/usernotes/RawUsernotes.test.ts index c531aa4..7065aa2 100644 --- a/packages/toolbox-storage/src/usernotes/RawUsernotes.test.ts +++ b/packages/toolbox-storage/src/usernotes/RawUsernotes.test.ts @@ -105,8 +105,4 @@ test('expandPermalink', t => { } }); -test.todo('compressBlob'); - -test.todo('decompressBlob'); - test.todo('migrateUsernotesSchema'); diff --git a/packages/toolbox-storage/src/usernotes/RawUsernotes.ts b/packages/toolbox-storage/src/usernotes/RawUsernotes.ts index 7a473e6..7accd79 100644 --- a/packages/toolbox-storage/src/usernotes/RawUsernotes.ts +++ b/packages/toolbox-storage/src/usernotes/RawUsernotes.ts @@ -1,6 +1,8 @@ // type imports for doc links import pako from 'pako'; +import {_compressBlob, _decompressBlob} from '#blobInternals'; + /** * The latest usernotes schema version that this library can handle. If a * usernotes page reports a schema version higher than this number, it can't be @@ -131,9 +133,8 @@ export function expandPermalink (shortenedLink: string): string { * @param value The object or value to compress * @returns The generated blob. */ -export function compressBlob (value: T): RawUsernotesBlob { - return Buffer.from(pako.deflate(JSON.stringify(value))).toString('base64'); -} +export const compressBlob = (value: T): RawUsernotesBlob => + _compressBlob(value); /** * Decompresses a {@linkcode RawUsernotesBlob} string into the JSON value it @@ -142,13 +143,8 @@ export function compressBlob (value: T): RawUsernotesBlob { * @param blob The blob to decompress * @returns The original JSON value. */ -export function decompressBlob (blob: RawUsernotesBlob): T { - return JSON.parse( - pako.inflate(Buffer.from(blob, 'base64').toString('binary'), { - to: 'string', - }), - ); -} +export const decompressBlob = (blob: RawUsernotesBlob): T => + _decompressBlob(blob); /** * Checks the schema version of raw usernotes data and attempts to update it to diff --git a/packages/toolbox-storage/src/usernotes/blobInternals.default.test.ts b/packages/toolbox-storage/src/usernotes/blobInternals.default.test.ts new file mode 100644 index 0000000..83ff335 --- /dev/null +++ b/packages/toolbox-storage/src/usernotes/blobInternals.default.test.ts @@ -0,0 +1,5 @@ +import test from 'ava'; + +test.todo('_compressBlob'); + +test.todo('_decompressBlob'); diff --git a/packages/toolbox-storage/src/usernotes/blobInternals.default.ts b/packages/toolbox-storage/src/usernotes/blobInternals.default.ts new file mode 100644 index 0000000..bc18046 --- /dev/null +++ b/packages/toolbox-storage/src/usernotes/blobInternals.default.ts @@ -0,0 +1,32 @@ +// Generic implementation of RawUsernotesBlob compression/decompression + +import 'core-js/actual/typed-array/from-base64'; +declare global { + interface Uint8ArrayConstructor { + fromBase64( + string: string, + options?: { + alphabet?: 'base64' | 'base64url'; + lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial'; + }, + ): Uint8Array; + } +} + +import 'core-js/actual/typed-array/to-base64'; +declare global { + interface Uint8Array { + toBase64( + options?: {alphabet?: 'base64' | 'base64url'; omitPadding?: boolean}, + ): string; + } +} + +import {deflate, inflate} from 'pako'; +import {type RawUsernotesBlob} from './RawUsernotes.js'; + +export const _compressBlob = (value: T): RawUsernotesBlob => + deflate(JSON.stringify(value)).toBase64(); + +export const _decompressBlob = (blob: RawUsernotesBlob): T => + JSON.parse(inflate(Uint8Array.fromBase64(blob), {to: 'string'})); diff --git a/packages/toolbox-storage/src/usernotes/blobInternals.node.test.ts b/packages/toolbox-storage/src/usernotes/blobInternals.node.test.ts new file mode 100644 index 0000000..83ff335 --- /dev/null +++ b/packages/toolbox-storage/src/usernotes/blobInternals.node.test.ts @@ -0,0 +1,5 @@ +import test from 'ava'; + +test.todo('_compressBlob'); + +test.todo('_decompressBlob'); diff --git a/packages/toolbox-storage/src/usernotes/blobInternals.node.ts b/packages/toolbox-storage/src/usernotes/blobInternals.node.ts new file mode 100644 index 0000000..dd67b62 --- /dev/null +++ b/packages/toolbox-storage/src/usernotes/blobInternals.node.ts @@ -0,0 +1,15 @@ +// Node-specific implementation of RawUsernotesBlob compression/decompression, +// which doesn't use any dependencies + +import {deflateSync, inflateSync} from 'node:zlib'; +import {type RawUsernotesBlob} from './RawUsernotes.js'; + +export const _compressBlob = (value: T): RawUsernotesBlob => { + const input = Buffer.from(JSON.stringify(value), 'utf-8'); + return deflateSync(input).toString('base64'); +}; + +export const _decompressBlob = (blob: RawUsernotesBlob): T => { + const input = Buffer.from(blob, 'base64'); + return JSON.parse(inflateSync(input).toString('utf-8')); +}; From f801a40c812d55041d9a0e059699ba8adad628d8 Mon Sep 17 00:00:00 2001 From: Erin Date: Fri, 28 Feb 2025 09:36:04 -0500 Subject: [PATCH 2/3] explicitly export everything from index.ts --- packages/toolbox-storage/src/index.ts | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/toolbox-storage/src/index.ts b/packages/toolbox-storage/src/index.ts index 928bcf5..77e284e 100644 --- a/packages/toolbox-storage/src/index.ts +++ b/packages/toolbox-storage/src/index.ts @@ -1,6 +1,30 @@ -export * from './config/RawSubredditConfig.js'; -export * from './config/SubredditConfig.js'; +export { + DEFAULT_CONFIG, + DEFAULT_USERNOTE_TYPES, + EARLIEST_KNOWN_CONFIG_SCHEMA, + LATEST_KNOWN_CONFIG_SCHEMA, + migrateConfigToLatestSchema, + RawDomainTag, + RawModMacro, + RawRemovalReason, + RawSubredditConfig, + RawUsernoteType, +} from './config/RawSubredditConfig.js'; +export {SubredditConfig} from './config/SubredditConfig.js'; -export * from './usernotes/RawUsernotes.js'; -export * from './usernotes/Usernote.js'; -export * from './usernotes/Usernotes.js'; +export { + compressBlob, + decompressBlob, + EARLIEST_KNOWN_USERNOTES_SCHEMA, + expandPermalink, + LATEST_KNOWN_USERNOTES_SCHEMA, + migrateUsernotesToLatestSchema, + RawUsernotes, + RawUsernotesBlob, + RawUsernotesConstants, + RawUsernotesNote, + RawUsernotesUsers, + squashPermalink, +} from './usernotes/RawUsernotes.js'; +export {Usernote, UsernoteInit} from './usernotes/Usernote.js'; +export {Usernotes} from './usernotes/Usernotes.js'; From e8bb8225755e5d40af4a3504ec1f90671a771d78 Mon Sep 17 00:00:00 2001 From: Erin Date: Fri, 28 Feb 2025 09:36:12 -0500 Subject: [PATCH 3/3] disallow other paths from being imported --- packages/toolbox-storage/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/toolbox-storage/package.json b/packages/toolbox-storage/package.json index 292b961..77805cd 100644 --- a/packages/toolbox-storage/package.json +++ b/packages/toolbox-storage/package.json @@ -8,6 +8,9 @@ "files": [ "dist/*" ], + "exports": { + ".": "./dist/index.js" + }, "imports": { "#blobInternals": { "node": "./dist/usernotes/blobInternals.node.js",