Skip to content

Commit

Permalink
add file class to node:buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Aug 6, 2024
1 parent 577d544 commit 9da84bd
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 25 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"devDependencies": {
"@bazel/bazelisk": "~1.19.0",
"@types/debug": "^4.1.10",
"@types/node": "^20.11.6",
"@types/node": "^20.14.8",
"@types/prettier": "^2.7.1",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/node/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const atob = globalThis.atob;
const btoa = globalThis.btoa;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const Blob = globalThis.Blob;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const File = globalThis.File;

export {
atob,
Expand All @@ -28,6 +30,7 @@ export {
kStringMaxLength,
Blob,
Buffer,
File,
SlowBuffer,
isAscii,
isUtf8,
Expand All @@ -45,6 +48,8 @@ export default {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
Blob,
Buffer,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
File,
SlowBuffer,
isAscii,
isUtf8,
Expand Down
26 changes: 14 additions & 12 deletions src/node/internal/crypto_random.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import {
arrayBufferToUnsignedBigInt,
} from 'node-internal:crypto_util';

type BufferLike = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array;

export type RandomBytesCallback = (err: any|null, buffer: Uint8Array) => void;
export function randomBytes(size: number, callback: RandomBytesCallback): void;
export function randomBytes(size: number): Uint8Array;
Expand All @@ -70,7 +72,7 @@ export function randomBytes(size: number, callback?: RandomBytesCallback) : Uint
}

export function randomFillSync(
buffer: ArrayBufferView|ArrayBuffer,
buffer: BufferLike,
offset?: number,
size?: number) {
if (!isAnyArrayBuffer(buffer) && !isArrayBufferView(buffer)) {
Expand All @@ -79,33 +81,33 @@ export function randomFillSync(
'DataView',
'ArrayBuffer',
'SharedArrayBuffer'
],buffer);
], buffer);
}
const maxLength = (buffer as Uint8Array).length;
const maxLength = buffer.length;
if (offset !== undefined) {
validateInteger(offset!, 'offset', 0, kMaxLength);
} else offset = 0;
if (size !== undefined) {
validateInteger(size!, 'size', 0, maxLength - offset);
} else size = maxLength;
if (isAnyArrayBuffer(buffer)) {
buffer = Buffer.from(buffer as ArrayBuffer);
buffer = Buffer.from(buffer);
}
buffer = (buffer as Buffer).subarray(offset, offset + size);
return crypto.getRandomValues(buffer as ArrayBufferView);
buffer = buffer.subarray(offset, offset + size);
return crypto.getRandomValues(buffer);
}

export type RandomFillCallback = (err: any|null, buf?: ArrayBufferView|ArrayBuffer) => void;
export function randomFill(buffer: ArrayBufferView|ArrayBuffer,
export function randomFill(buffer: BufferLike,
callback?: RandomFillCallback) : void;
export function randomFill(buffer: ArrayBufferView|ArrayBuffer,
export function randomFill(buffer: BufferLike,
offset: number,
callback?: RandomFillCallback) : void;
export function randomFill(buffer: ArrayBufferView|ArrayBuffer,
export function randomFill(buffer: BufferLike,
offset: number,
size: number,
callback?: RandomFillCallback) : void;
export function randomFill(buffer: ArrayBufferView|ArrayBuffer,
export function randomFill(buffer: BufferLike,
offsetOrCallback?: number|RandomFillCallback,
sizeOrCallback?: number|RandomFillCallback,
callback?: RandomFillCallback) {
Expand All @@ -115,12 +117,12 @@ export function randomFill(buffer: ArrayBufferView|ArrayBuffer,
'DataView',
'ArrayBuffer',
'SharedArrayBuffer'
],buffer);
], buffer);
}

let offset = 0;
let size = 0;
const maxLength = (buffer as Uint8Array).length;
const maxLength = buffer.length;
if (typeof callback === 'function') {
validateInteger(offsetOrCallback, 'offset', 0, maxLength);
offset = offsetOrCallback as number;
Expand Down
8 changes: 0 additions & 8 deletions src/node/internal/web_crypto.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
// Just enough of web crypto api to write the code in node-compat

declare namespace crypto {
function getRandomValues<T extends ArrayBufferView | null>(array: T): T;
function randomUUID(): string;

const subtle: SubtleCrypto;
}

type SubtleCrypto = unknown;

type CryptoKey = unknown;
Expand Down
4 changes: 4 additions & 0 deletions src/workerd/api/blob.c++
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ jsg::Ref<File> File::constructor(jsg::Lock& js, jsg::Optional<Bits> bits,
double lastModified;
KJ_IF_SOME(m, maybeLastModified) {
lastModified = m;

if (kj::isNaN(lastModified)) {
lastModified = 0;
}
} else {
lastModified = dateNow();
}
Expand Down
102 changes: 102 additions & 0 deletions src/workerd/api/node/tests/buffer-nodejs-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
isAscii,
isUtf8,
transcode,
File,
} from 'node:buffer';

import * as buffer from 'node:buffer';
Expand Down Expand Up @@ -5792,3 +5793,104 @@ export const transcodeTest = {
}
}
};

// Tests are taken from Node.js
// https://github.com/nodejs/node/blob/main/test/parallel/test-file.js
export const fileTest = {
test() {
throws(() => new File(), TypeError);
throws(() => new File([]), TypeError);
throws(() => File.prototype.name, TypeError);
throws(() => File.prototype.lastModified, TypeError);

{
const keys = Object.keys(File.prototype).sort();
deepStrictEqual(keys, ['lastModified', 'name']);
}

{
const file = new File([], 'dummy.txt.exe');
strictEqual(file.name, 'dummy.txt.exe');
strictEqual(file.size, 0);
strictEqual(typeof file.lastModified, 'number');
ok(file.lastModified <= Date.now());
}

{
const toPrimitive = {
[Symbol.toPrimitive]() {
return 'NaN';
}
};

const invalidLastModified = [
null,
'string',
false,
toPrimitive,
];

for (const lastModified of invalidLastModified) {
const file = new File([], '', { lastModified });
strictEqual(file.lastModified, 0);
}
}

{
const file = new File([], '', { lastModified: undefined });
notStrictEqual(file.lastModified, 0);
}

{
const toPrimitive = {
[Symbol.toPrimitive]() {
throw new TypeError('boom');
}
};

const throwValues = [
BigInt(3n),
toPrimitive,
];

for (const lastModified of throwValues) {
throws(() => new File([], '', { lastModified }), TypeError);
}
}

{
const valid = [
{
[Symbol.toPrimitive]() {
return 10;
}
},
new Number(10),
10,
];

for (const lastModified of valid) {
strictEqual(new File([], '', { lastModified }).lastModified, 10);
}
}

{
function MyClass() {}
MyClass.prototype.lastModified = 10;

const file = new File([], '', new MyClass());
strictEqual(file.lastModified, 10);
}

{
let counter = 0;
new File([], '', {
get lastModified() {
counter++;
return 10;
}
});
strictEqual(counter, 1);
}
}
}

0 comments on commit 9da84bd

Please sign in to comment.