Skip to content

Commit 5dbe9fe

Browse files
committed
fixup! fixup! Replace tslint with eslint
1 parent 19178f9 commit 5dbe9fe

File tree

5 files changed

+116
-87
lines changed

5 files changed

+116
-87
lines changed

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { FeatureType, TFRecordsBuilder, ITFRecordsFileWriter, TransformStreamOptions } from "./tensorFlowBuilder";
1+
export { FeatureType, TFRecordsBuilder, TFRecordsTransform, TransformStreamOptions } from "./tensorFlowBuilder";
22
export { TFRecordsReader } from "./tensorFlowReader";

src/tensorFlowBuilder.js

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict";
22
Object.defineProperty(exports, "__esModule", { value: true });
3-
exports.TFRecordsBuilder = exports.FeatureType = void 0;
3+
exports.TFRecordsBuilder = exports.FeatureType = exports.TFRecordsTransform = void 0;
44
const tensorFlowRecordsProtoBuf_pb_1 = require("./tensorFlowRecordsProtoBuf_pb");
55
const tensorFlowHelpers_1 = require("./tensorFlowHelpers");
66
const stream_1 = require("stream");
@@ -12,6 +12,26 @@ try {
1212
catch (_a) {
1313
// Not available in browser
1414
}
15+
/**
16+
* A Transform stream for TFRecords with an optional `finished` promise
17+
* that resolves when the stream (and any piped file) is complete.
18+
*/
19+
class TFRecordsTransform extends stream_1.Transform {
20+
constructor(options, fileStream) {
21+
super(options);
22+
// If there's a file stream, wait for it to finish; otherwise wait for this transform
23+
const streamToWatch = fileStream || this;
24+
this.finished = new Promise((resolve, reject) => {
25+
(0, stream_1.finished)(streamToWatch, (err) => {
26+
if (err)
27+
reject(err);
28+
else
29+
resolve();
30+
});
31+
});
32+
}
33+
}
34+
exports.TFRecordsTransform = TFRecordsTransform;
1535
/**
1636
* @name - TFRecords Feature Type
1737
* @description - Defines the type of TFRecords Feature
@@ -59,11 +79,27 @@ class TFRecordsBuilder {
5979
records.forEach((r) => transformer.write(r));
6080
return transformer;
6181
}
82+
/**
83+
* @description - Create a Transform stream for TFRecords.
84+
* Optionally pipes output directly to disk when filePath is provided.
85+
* @param optionsOrHighWaterMark - Stream buffer size (number) or options object
86+
* @param options.highWaterMark - Stream buffer size
87+
* @param options.filePath - When provided, pipes output to this file (Node.js only).
88+
* Use stream.finished promise to know when done.
89+
* @returns - TFRecordsTransform stream with a `finished` promise
90+
*/
6291
static transformStream(optionsOrHighWaterMark) {
6392
const options = typeof optionsOrHighWaterMark === "number"
6493
? { highWaterMark: optionsOrHighWaterMark }
6594
: optionsOrHighWaterMark;
66-
const transformer = new stream_1.Transform({
95+
let fileStream;
96+
if (options === null || options === void 0 ? void 0 : options.filePath) {
97+
if (!fs) {
98+
throw new Error("File output is only available in Node.js. Use transformStream() without filePath in the browser.");
99+
}
100+
fileStream = fs.createWriteStream(options.filePath);
101+
}
102+
const transformer = new TFRecordsTransform({
67103
transform: (record, encoding, callback) => {
68104
const length = record.length;
69105
// Get TFRecords CRCs for TFRecords Header and Footer
@@ -73,30 +109,11 @@ class TFRecordsBuilder {
73109
callback(undefined, Buffer.concat([bufferLength, bufferLengthMaskedCRC, record, bufferDataMaskedCRC]));
74110
},
75111
highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
76-
});
77-
if (!(options === null || options === void 0 ? void 0 : options.filePath)) {
78-
return transformer;
112+
}, fileStream);
113+
if (fileStream) {
114+
transformer.pipe(fileStream);
79115
}
80-
// File output mode
81-
if (!fs) {
82-
throw new Error("File output is only available in Node.js. Use transformStream() without filePath in the browser.");
83-
}
84-
const fileStream = fs.createWriteStream(options.filePath);
85-
transformer.pipe(fileStream);
86-
return {
87-
write: (record) => transformer.write(record),
88-
end: () => new Promise((resolve, reject) => {
89-
transformer.end();
90-
(0, stream_1.finished)(fileStream, (err) => {
91-
if (err) {
92-
reject(err);
93-
}
94-
else {
95-
resolve();
96-
}
97-
});
98-
}),
99-
};
116+
return transformer;
100117
}
101118
constructor() {
102119
this.features = new tensorFlowRecordsProtoBuf_pb_1.Features();

src/tensorFlowBuilder.test.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,18 +126,19 @@ describe("TFRecords Builder Functions", function () {
126126
});
127127
}); });
128128
it("writes records to disk without holding them in memory", function () { return __awaiter(void 0, void 0, void 0, function () {
129-
var writer, i, builder, stats;
129+
var stream, i, builder, stats;
130130
return __generator(this, function (_a) {
131131
switch (_a.label) {
132132
case 0:
133-
writer = tensorFlowBuilder_1.TFRecordsBuilder.transformStream({ filePath: tempFile });
133+
stream = tensorFlowBuilder_1.TFRecordsBuilder.transformStream({ filePath: tempFile });
134134
// Create and write multiple records
135135
for (i = 0; i < 3; i++) {
136136
builder = new tensorFlowBuilder_1.TFRecordsBuilder();
137137
builder.addFeature("index", tensorFlowBuilder_1.FeatureType.Int64, i);
138-
writer.write(builder.build());
138+
stream.write(builder.build());
139139
}
140-
return [4 /*yield*/, writer.end()];
140+
stream.end();
141+
return [4 /*yield*/, stream.finished];
141142
case 1:
142143
_a.sent();
143144
return [4 /*yield*/, fs.promises.stat(tempFile)];
@@ -149,7 +150,7 @@ describe("TFRecords Builder Functions", function () {
149150
});
150151
}); });
151152
it("produces same output as in-memory buildTFRecords", function () { return __awaiter(void 0, void 0, void 0, function () {
152-
var builder, record, inMemoryResult, writer, diskResult;
153+
var builder, record, inMemoryResult, stream, diskResult;
153154
return __generator(this, function (_a) {
154155
switch (_a.label) {
155156
case 0:
@@ -159,9 +160,10 @@ describe("TFRecords Builder Functions", function () {
159160
builder.addArrayFeature("image/height", tensorFlowBuilder_1.FeatureType.String, ["1", "2"]);
160161
record = builder.build();
161162
inMemoryResult = tensorFlowBuilder_1.TFRecordsBuilder.buildTFRecords([record]);
162-
writer = tensorFlowBuilder_1.TFRecordsBuilder.transformStream({ filePath: tempFile });
163-
writer.write(record);
164-
return [4 /*yield*/, writer.end()];
163+
stream = tensorFlowBuilder_1.TFRecordsBuilder.transformStream({ filePath: tempFile });
164+
stream.write(record);
165+
stream.end();
166+
return [4 /*yield*/, stream.finished];
165167
case 1:
166168
_a.sent();
167169
return [4 /*yield*/, fs.promises.readFile(tempFile)];

src/tensorFlowBuilder.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,17 @@ describe("TFRecords Builder Functions", () => {
8989
});
9090

9191
it("writes records to disk without holding them in memory", async () => {
92-
const writer = TFRecordsBuilder.transformStream({ filePath: tempFile });
92+
const stream = TFRecordsBuilder.transformStream({ filePath: tempFile });
9393

9494
// Create and write multiple records
9595
for (let i = 0; i < 3; i++) {
9696
const builder = new TFRecordsBuilder();
9797
builder.addFeature("index", FeatureType.Int64, i);
98-
writer.write(builder.build());
98+
stream.write(builder.build());
9999
}
100100

101-
await writer.end();
101+
stream.end();
102+
await stream.finished;
102103

103104
// Verify file exists and has content
104105
const stats = await fs.promises.stat(tempFile);
@@ -116,9 +117,10 @@ describe("TFRecords Builder Functions", () => {
116117
const inMemoryResult = TFRecordsBuilder.buildTFRecords([record]);
117118

118119
// Build using file writer
119-
const writer = TFRecordsBuilder.transformStream({ filePath: tempFile });
120-
writer.write(record);
121-
await writer.end();
120+
const stream = TFRecordsBuilder.transformStream({ filePath: tempFile });
121+
stream.write(record);
122+
stream.end();
123+
await stream.finished;
122124

123125
// Read file contents and compare
124126
const diskResult = await fs.promises.readFile(tempFile);

src/tensorFlowBuilder.ts

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TFRecordsImageMessage, Features, Feature,
22
BytesList, Int64List, FloatList } from "./tensorFlowRecordsProtoBuf_pb";
33
import { crc32c, getInt32Buffer, getInt64Buffer, maskCrc, textEncode } from "./tensorFlowHelpers";
4-
import { Transform, Readable, finished } from "stream";
4+
import { Transform, Readable, TransformOptions, finished } from "stream";
55

66
// Conditionally import fs for Node.js environments
77
let fs: typeof import("fs") | null = null;
@@ -11,16 +11,36 @@ try {
1111
// Not available in browser
1212
}
1313

14-
export interface ITFRecordsFileWriter {
15-
write(record: Buffer): boolean;
16-
end(): Promise<void>;
17-
}
18-
1914
export interface TransformStreamOptions {
2015
highWaterMark?: number;
2116
filePath?: string;
2217
}
2318

19+
/**
20+
* A Transform stream for TFRecords with an optional `finished` promise
21+
* that resolves when the stream (and any piped file) is complete.
22+
*/
23+
export class TFRecordsTransform extends Transform {
24+
/**
25+
* A promise that resolves when the stream is finished writing.
26+
* When piped to a file, this waits for the file to be fully written.
27+
*/
28+
public finished: Promise<void>;
29+
30+
constructor(options?: TransformOptions, fileStream?: NodeJS.WritableStream) {
31+
super(options);
32+
33+
// If there's a file stream, wait for it to finish; otherwise wait for this transform
34+
const streamToWatch = fileStream || this;
35+
this.finished = new Promise((resolve, reject) => {
36+
finished(streamToWatch, (err) => {
37+
if (err) reject(err);
38+
else resolve();
39+
});
40+
});
41+
}
42+
}
43+
2444
/**
2545
* @name - TFRecords Feature Type
2646
* @description - Defines the type of TFRecords Feature
@@ -74,60 +94,48 @@ export class TFRecordsBuilder {
7494

7595
/**
7696
* @description - Create a Transform stream for TFRecords.
77-
* Optionally writes directly to disk when filePath is provided.
97+
* Optionally pipes output directly to disk when filePath is provided.
7898
* @param optionsOrHighWaterMark - Stream buffer size (number) or options object
7999
* @param options.highWaterMark - Stream buffer size
80-
* @param options.filePath - When provided, pipes output to this file (Node.js only)
81-
* @returns - Transform stream, or ITFRecordsFileWriter when filePath is provided
100+
* @param options.filePath - When provided, pipes output to this file (Node.js only).
101+
* Use stream.finished promise to know when done.
102+
* @returns - TFRecordsTransform stream with a `finished` promise
82103
*/
83-
public static transformStream(): Transform;
84-
public static transformStream(highWaterMark: number): Transform;
85-
public static transformStream(options: { highWaterMark?: number }): Transform;
86-
public static transformStream(options: { filePath: string; highWaterMark?: number }): ITFRecordsFileWriter;
87-
public static transformStream(optionsOrHighWaterMark?: TransformStreamOptions | number): Transform | ITFRecordsFileWriter {
104+
public static transformStream(optionsOrHighWaterMark?: TransformStreamOptions | number): TFRecordsTransform {
88105
const options: TransformStreamOptions | undefined =
89106
typeof optionsOrHighWaterMark === "number"
90107
? { highWaterMark: optionsOrHighWaterMark }
91108
: optionsOrHighWaterMark;
92109

93-
const transformer = new Transform({
94-
transform: (record: Buffer, encoding, callback) => {
95-
const length = record.length;
110+
let fileStream: NodeJS.WritableStream | undefined;
111+
if (options?.filePath) {
112+
if (!fs) {
113+
throw new Error("File output is only available in Node.js. Use transformStream() without filePath in the browser.");
114+
}
115+
fileStream = fs.createWriteStream(options.filePath);
116+
}
96117

97-
// Get TFRecords CRCs for TFRecords Header and Footer
98-
const bufferLength = getInt64Buffer(length);
99-
const bufferLengthMaskedCRC = getInt32Buffer(maskCrc(crc32c(bufferLength)));
100-
const bufferDataMaskedCRC = getInt32Buffer(maskCrc(crc32c(record)));
101-
callback(undefined, Buffer.concat([bufferLength, bufferLengthMaskedCRC, record, bufferDataMaskedCRC]));
118+
const transformer = new TFRecordsTransform(
119+
{
120+
transform: (record: Buffer, encoding, callback) => {
121+
const length = record.length;
122+
123+
// Get TFRecords CRCs for TFRecords Header and Footer
124+
const bufferLength = getInt64Buffer(length);
125+
const bufferLengthMaskedCRC = getInt32Buffer(maskCrc(crc32c(bufferLength)));
126+
const bufferDataMaskedCRC = getInt32Buffer(maskCrc(crc32c(record)));
127+
callback(undefined, Buffer.concat([bufferLength, bufferLengthMaskedCRC, record, bufferDataMaskedCRC]));
128+
},
129+
highWaterMark: options?.highWaterMark,
102130
},
103-
highWaterMark: options?.highWaterMark,
104-
});
131+
fileStream,
132+
);
105133

106-
if (!options?.filePath) {
107-
return transformer;
134+
if (fileStream) {
135+
transformer.pipe(fileStream);
108136
}
109137

110-
// File output mode
111-
if (!fs) {
112-
throw new Error("File output is only available in Node.js. Use transformStream() without filePath in the browser.");
113-
}
114-
115-
const fileStream = fs.createWriteStream(options.filePath);
116-
transformer.pipe(fileStream);
117-
118-
return {
119-
write: (record: Buffer) => transformer.write(record),
120-
end: () => new Promise<void>((resolve, reject) => {
121-
transformer.end();
122-
finished(fileStream, (err) => {
123-
if (err) {
124-
reject(err);
125-
} else {
126-
resolve();
127-
}
128-
});
129-
}),
130-
};
138+
return transformer;
131139
}
132140

133141
private features: Features;

0 commit comments

Comments
 (0)