@milaboratories/pframes-rs-serv 1.0.69 → 1.0.71

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/dist/fs-store.cjs CHANGED
@@ -1,13 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var node_fs = require('node:fs');
4
3
  var promises = require('node:fs/promises');
5
4
  var node_path = require('node:path');
6
5
  var plModelMiddleLayer = require('@milaboratories/pl-model-middle-layer');
7
6
  var plModelCommon = require('@milaboratories/pl-model-common');
8
7
 
9
8
  /** Object store for serving files from a local directory */
10
- class FileSystemStore extends plModelMiddleLayer.PFrameInternal.ObjectStore {
9
+ class FileSystemStore extends plModelMiddleLayer.PFrameInternal.BaseObjectStore {
11
10
  rootDir;
12
11
  constructor(options) {
13
12
  super(options);
@@ -28,6 +27,16 @@ class FileSystemStore extends plModelMiddleLayer.PFrameInternal.ObjectStore {
28
27
  }
29
28
  async request(filename, params) {
30
29
  let file;
30
+ const respond = async (response) => {
31
+ try {
32
+ await params
33
+ .callback(response)
34
+ .finally(async () => await file?.close());
35
+ }
36
+ catch (error) {
37
+ this.logger('warn', `File system store received unexpected rejection from callback: ${plModelCommon.ensureError(error)}`);
38
+ }
39
+ };
31
40
  try {
32
41
  try {
33
42
  const path = node_path.join(this.rootDir, filename);
@@ -35,7 +44,7 @@ class FileSystemStore extends plModelMiddleLayer.PFrameInternal.ObjectStore {
35
44
  }
36
45
  catch (error) {
37
46
  this.logger('error', `File system store failed to open file ${filename}: ${plModelCommon.ensureError(error)}`);
38
- return await params.callback({ type: 'NotFound' });
47
+ return await respond({ type: 'NotFound' });
39
48
  }
40
49
  params.signal.throwIfAborted();
41
50
  let size;
@@ -44,49 +53,36 @@ class FileSystemStore extends plModelMiddleLayer.PFrameInternal.ObjectStore {
44
53
  }
45
54
  catch (error) {
46
55
  this.logger('error', `File system store failed to get size of file ${filename}: ${plModelCommon.ensureError(error)}`);
47
- return await params.callback({ type: 'InternalError' });
56
+ return await respond({ type: 'InternalError' });
48
57
  }
49
58
  params.signal.throwIfAborted();
50
59
  const range = this.translate(size, params.range);
51
60
  if (!range) {
52
- return await params.callback({ type: 'RangeNotSatisfiable', size });
61
+ return await respond({ type: 'RangeNotSatisfiable', size });
53
62
  }
54
63
  if (params.method === 'HEAD') {
55
- return await params.callback({ type: 'Ok', size, range });
64
+ return await respond({ type: 'Ok', size, range });
56
65
  }
57
66
  let data;
58
67
  try {
59
- data = node_fs.createReadStream('ignored', {
60
- fd: file.fd,
68
+ data = file.createReadStream({
61
69
  start: range.start,
62
70
  end: range.end,
63
- signal: params.signal
71
+ autoClose: false
64
72
  });
65
73
  this.logger('info', `File system store created read stream for ${filename}[${range.start}..=${range.end}]`);
66
74
  }
67
75
  catch (error) {
68
76
  this.logger('error', `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${plModelCommon.ensureError(error)}`);
69
- return await params.callback({ type: 'InternalError' });
70
- }
71
- try {
72
- return await params.callback({ type: 'Ok', size, range, data });
73
- }
74
- catch (error) {
75
- if (!plModelCommon.isAbortError(error)) {
76
- this.logger('error', `File system store received unexpected rejection from callback: ${plModelCommon.ensureError(error)}`);
77
- }
77
+ return await respond({ type: 'InternalError' });
78
78
  }
79
+ return await respond({ type: 'Ok', size, range, data });
79
80
  }
80
81
  catch (error) {
81
82
  if (!plModelCommon.isAbortError(error)) {
82
- this.logger('error', `File system store unhandled error: ${plModelCommon.ensureError(error)}`);
83
+ this.logger('warn', `File system store unhandled error: ${plModelCommon.ensureError(error)}`);
83
84
  }
84
- return await params.callback({ type: 'InternalError' });
85
- }
86
- finally {
87
- await file?.close().catch(() => {
88
- // already closed
89
- });
85
+ return await respond({ type: 'InternalError' });
90
86
  }
91
87
  }
92
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fs-store.cjs","sources":["../src/fs-store.ts"],"sourcesContent":["import type { Readable } from 'node:stream';\nimport { createReadStream } from 'node:fs';\nimport { stat, open, type FileHandle } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport { ensureError, isAbortError } from '@milaboratories/pl-model-common';\n\n/** Object store for serving files from a local directory */\nexport class FileSystemStore extends PFrameInternal.ObjectStore {\n private readonly rootDir: string;\n\n private constructor(options: PFrameInternal.FsStoreOptions) {\n super(options);\n\n this.rootDir = options.rootDir;\n }\n\n static async init(\n options: PFrameInternal.FsStoreOptions\n ): Promise<FileSystemStore> {\n const resolvedRootDir = resolve(options.rootDir);\n\n const rootStats = await stat(resolvedRootDir).catch(() => {\n throw new Error(\n `File system store root directory does not exist: ${resolvedRootDir}`\n );\n });\n if (!rootStats.isDirectory()) {\n throw new Error(\n `File system store root path is not a directory: ${resolvedRootDir}`\n );\n }\n\n return new FileSystemStore({\n ...options,\n rootDir: resolvedRootDir\n });\n }\n\n override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n }\n ): Promise<void> {\n let file: FileHandle | undefined;\n try {\n try {\n const path = join(this.rootDir, filename);\n file = await open(path, 'r');\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to open file ${filename}: ${ensureError(error)}`\n );\n return await params.callback({ type: 'NotFound' });\n }\n params.signal.throwIfAborted();\n\n let size: number;\n try {\n ({ size } = await file.stat());\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to get size of file ${filename}: ${ensureError(error)}`\n );\n return await params.callback({ type: 'InternalError' });\n }\n params.signal.throwIfAborted();\n\n const range = this.translate(size, params.range);\n if (!range) {\n return await params.callback({ type: 'RangeNotSatisfiable', size });\n }\n\n if (params.method === 'HEAD') {\n return await params.callback({ type: 'Ok', size, range });\n }\n\n let data: Readable;\n try {\n data = createReadStream('ignored', {\n fd: file.fd,\n start: range.start,\n end: range.end,\n signal: params.signal\n });\n this.logger(\n 'info',\n `File system store created read stream for ${filename}[${range.start}..=${range.end}]`\n );\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${ensureError(error)}`\n );\n return await params.callback({ type: 'InternalError' });\n }\n\n try {\n return await params.callback({ type: 'Ok', size, range, data });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n this.logger(\n 'error',\n `File system store received unexpected rejection from callback: ${ensureError(error)}`\n );\n }\n }\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n this.logger(\n 'error',\n `File system store unhandled error: ${ensureError(error)}`\n );\n }\n return await params.callback({ type: 'InternalError' });\n } finally {\n await file?.close().catch(() => {\n // already closed\n });\n }\n }\n}\n"],"names":["PFrameInternal","resolve","stat","join","open","ensureError","createReadStream","isAbortError"],"mappings":"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"}
1
+ {"version":3,"file":"fs-store.cjs","sources":["../src/fs-store.ts"],"sourcesContent":["import type { Readable } from 'node:stream';\nimport { stat, open, type FileHandle } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport { ensureError, isAbortError } from '@milaboratories/pl-model-common';\n\n/** Object store for serving files from a local directory */\nexport class FileSystemStore extends PFrameInternal.BaseObjectStore {\n private readonly rootDir: string;\n\n private constructor(options: PFrameInternal.FsStoreOptions) {\n super(options);\n\n this.rootDir = options.rootDir;\n }\n\n static async init(\n options: PFrameInternal.FsStoreOptions\n ): Promise<FileSystemStore> {\n const resolvedRootDir = resolve(options.rootDir);\n\n const rootStats = await stat(resolvedRootDir).catch(() => {\n throw new Error(\n `File system store root directory does not exist: ${resolvedRootDir}`\n );\n });\n if (!rootStats.isDirectory()) {\n throw new Error(\n `File system store root path is not a directory: ${resolvedRootDir}`\n );\n }\n\n return new FileSystemStore({\n ...options,\n rootDir: resolvedRootDir\n });\n }\n\n override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n }\n ): Promise<void> {\n let file: FileHandle | undefined;\n const respond = async (response: PFrameInternal.ObjectStoreResponse) => {\n try {\n await params\n .callback(response)\n .finally(async () => await file?.close());\n } catch (error: unknown) {\n this.logger(\n 'warn',\n `File system store received unexpected rejection from callback: ${ensureError(error)}`\n );\n }\n };\n\n try {\n try {\n const path = join(this.rootDir, filename);\n file = await open(path, 'r');\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to open file ${filename}: ${ensureError(error)}`\n );\n return await respond({ type: 'NotFound' });\n }\n params.signal.throwIfAborted();\n\n let size: number;\n try {\n ({ size } = await file.stat());\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to get size of file ${filename}: ${ensureError(error)}`\n );\n return await respond({ type: 'InternalError' });\n }\n params.signal.throwIfAborted();\n\n const range = this.translate(size, params.range);\n if (!range) {\n return await respond({ type: 'RangeNotSatisfiable', size });\n }\n\n if (params.method === 'HEAD') {\n return await respond({ type: 'Ok', size, range });\n }\n\n let data: Readable;\n try {\n data = file.createReadStream({\n start: range.start,\n end: range.end,\n autoClose: false\n });\n this.logger(\n 'info',\n `File system store created read stream for ${filename}[${range.start}..=${range.end}]`\n );\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${ensureError(error)}`\n );\n return await respond({ type: 'InternalError' });\n }\n\n return await respond({ type: 'Ok', size, range, data });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n this.logger(\n 'warn',\n `File system store unhandled error: ${ensureError(error)}`\n );\n }\n return await respond({ type: 'InternalError' });\n }\n }\n}\n"],"names":["PFrameInternal","resolve","stat","ensureError","join","open","isAbortError"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
2
2
  /** Object store for serving files from a local directory */
3
- export declare class FileSystemStore extends PFrameInternal.ObjectStore {
3
+ export declare class FileSystemStore extends PFrameInternal.BaseObjectStore {
4
4
  private readonly rootDir;
5
5
  private constructor();
6
6
  static init(options: PFrameInternal.FsStoreOptions): Promise<FileSystemStore>;
@@ -1 +1 @@
1
- {"version":3,"file":"fs-store.d.ts","sourceRoot":"","sources":["../src/fs-store.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAGvE,4DAA4D;AAC5D,qBAAa,eAAgB,SAAQ,cAAc,CAAC,WAAW;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,OAAO;WAMM,IAAI,CACf,OAAO,EAAE,cAAc,CAAC,cAAc,GACrC,OAAO,CAAC,eAAe,CAAC;IAoBZ,OAAO,CACpB,QAAQ,EAAE,cAAc,CAAC,eAAe,EACxC,MAAM,EAAE;QACN,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC;QAClC,KAAK,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC;QACjC,MAAM,EAAE,WAAW,CAAC;QACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3E,GACA,OAAO,CAAC,IAAI,CAAC;CAgFjB"}
1
+ {"version":3,"file":"fs-store.d.ts","sourceRoot":"","sources":["../src/fs-store.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAGvE,4DAA4D;AAC5D,qBAAa,eAAgB,SAAQ,cAAc,CAAC,eAAe;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,OAAO;WAMM,IAAI,CACf,OAAO,EAAE,cAAc,CAAC,cAAc,GACrC,OAAO,CAAC,eAAe,CAAC;IAoBZ,OAAO,CACpB,QAAQ,EAAE,cAAc,CAAC,eAAe,EACxC,MAAM,EAAE;QACN,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC;QAClC,KAAK,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC;QACjC,MAAM,EAAE,WAAW,CAAC;QACpB,QAAQ,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3E,GACA,OAAO,CAAC,IAAI,CAAC;CA+EjB"}
package/dist/fs-store.js CHANGED
@@ -1,11 +1,10 @@
1
- import { createReadStream } from 'node:fs';
2
1
  import { stat, open } from 'node:fs/promises';
3
2
  import { resolve, join } from 'node:path';
4
3
  import { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
5
4
  import { ensureError, isAbortError } from '@milaboratories/pl-model-common';
6
5
 
7
6
  /** Object store for serving files from a local directory */
8
- class FileSystemStore extends PFrameInternal.ObjectStore {
7
+ class FileSystemStore extends PFrameInternal.BaseObjectStore {
9
8
  rootDir;
10
9
  constructor(options) {
11
10
  super(options);
@@ -26,6 +25,16 @@ class FileSystemStore extends PFrameInternal.ObjectStore {
26
25
  }
27
26
  async request(filename, params) {
28
27
  let file;
28
+ const respond = async (response) => {
29
+ try {
30
+ await params
31
+ .callback(response)
32
+ .finally(async () => await file?.close());
33
+ }
34
+ catch (error) {
35
+ this.logger('warn', `File system store received unexpected rejection from callback: ${ensureError(error)}`);
36
+ }
37
+ };
29
38
  try {
30
39
  try {
31
40
  const path = join(this.rootDir, filename);
@@ -33,7 +42,7 @@ class FileSystemStore extends PFrameInternal.ObjectStore {
33
42
  }
34
43
  catch (error) {
35
44
  this.logger('error', `File system store failed to open file ${filename}: ${ensureError(error)}`);
36
- return await params.callback({ type: 'NotFound' });
45
+ return await respond({ type: 'NotFound' });
37
46
  }
38
47
  params.signal.throwIfAborted();
39
48
  let size;
@@ -42,49 +51,36 @@ class FileSystemStore extends PFrameInternal.ObjectStore {
42
51
  }
43
52
  catch (error) {
44
53
  this.logger('error', `File system store failed to get size of file ${filename}: ${ensureError(error)}`);
45
- return await params.callback({ type: 'InternalError' });
54
+ return await respond({ type: 'InternalError' });
46
55
  }
47
56
  params.signal.throwIfAborted();
48
57
  const range = this.translate(size, params.range);
49
58
  if (!range) {
50
- return await params.callback({ type: 'RangeNotSatisfiable', size });
59
+ return await respond({ type: 'RangeNotSatisfiable', size });
51
60
  }
52
61
  if (params.method === 'HEAD') {
53
- return await params.callback({ type: 'Ok', size, range });
62
+ return await respond({ type: 'Ok', size, range });
54
63
  }
55
64
  let data;
56
65
  try {
57
- data = createReadStream('ignored', {
58
- fd: file.fd,
66
+ data = file.createReadStream({
59
67
  start: range.start,
60
68
  end: range.end,
61
- signal: params.signal
69
+ autoClose: false
62
70
  });
63
71
  this.logger('info', `File system store created read stream for ${filename}[${range.start}..=${range.end}]`);
64
72
  }
65
73
  catch (error) {
66
74
  this.logger('error', `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${ensureError(error)}`);
67
- return await params.callback({ type: 'InternalError' });
68
- }
69
- try {
70
- return await params.callback({ type: 'Ok', size, range, data });
71
- }
72
- catch (error) {
73
- if (!isAbortError(error)) {
74
- this.logger('error', `File system store received unexpected rejection from callback: ${ensureError(error)}`);
75
- }
75
+ return await respond({ type: 'InternalError' });
76
76
  }
77
+ return await respond({ type: 'Ok', size, range, data });
77
78
  }
78
79
  catch (error) {
79
80
  if (!isAbortError(error)) {
80
- this.logger('error', `File system store unhandled error: ${ensureError(error)}`);
81
+ this.logger('warn', `File system store unhandled error: ${ensureError(error)}`);
81
82
  }
82
- return await params.callback({ type: 'InternalError' });
83
- }
84
- finally {
85
- await file?.close().catch(() => {
86
- // already closed
87
- });
83
+ return await respond({ type: 'InternalError' });
88
84
  }
89
85
  }
90
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fs-store.js","sources":["../src/fs-store.ts"],"sourcesContent":["import type { Readable } from 'node:stream';\nimport { createReadStream } from 'node:fs';\nimport { stat, open, type FileHandle } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport { ensureError, isAbortError } from '@milaboratories/pl-model-common';\n\n/** Object store for serving files from a local directory */\nexport class FileSystemStore extends PFrameInternal.ObjectStore {\n private readonly rootDir: string;\n\n private constructor(options: PFrameInternal.FsStoreOptions) {\n super(options);\n\n this.rootDir = options.rootDir;\n }\n\n static async init(\n options: PFrameInternal.FsStoreOptions\n ): Promise<FileSystemStore> {\n const resolvedRootDir = resolve(options.rootDir);\n\n const rootStats = await stat(resolvedRootDir).catch(() => {\n throw new Error(\n `File system store root directory does not exist: ${resolvedRootDir}`\n );\n });\n if (!rootStats.isDirectory()) {\n throw new Error(\n `File system store root path is not a directory: ${resolvedRootDir}`\n );\n }\n\n return new FileSystemStore({\n ...options,\n rootDir: resolvedRootDir\n });\n }\n\n override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n }\n ): Promise<void> {\n let file: FileHandle | undefined;\n try {\n try {\n const path = join(this.rootDir, filename);\n file = await open(path, 'r');\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to open file ${filename}: ${ensureError(error)}`\n );\n return await params.callback({ type: 'NotFound' });\n }\n params.signal.throwIfAborted();\n\n let size: number;\n try {\n ({ size } = await file.stat());\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to get size of file ${filename}: ${ensureError(error)}`\n );\n return await params.callback({ type: 'InternalError' });\n }\n params.signal.throwIfAborted();\n\n const range = this.translate(size, params.range);\n if (!range) {\n return await params.callback({ type: 'RangeNotSatisfiable', size });\n }\n\n if (params.method === 'HEAD') {\n return await params.callback({ type: 'Ok', size, range });\n }\n\n let data: Readable;\n try {\n data = createReadStream('ignored', {\n fd: file.fd,\n start: range.start,\n end: range.end,\n signal: params.signal\n });\n this.logger(\n 'info',\n `File system store created read stream for ${filename}[${range.start}..=${range.end}]`\n );\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${ensureError(error)}`\n );\n return await params.callback({ type: 'InternalError' });\n }\n\n try {\n return await params.callback({ type: 'Ok', size, range, data });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n this.logger(\n 'error',\n `File system store received unexpected rejection from callback: ${ensureError(error)}`\n );\n }\n }\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n this.logger(\n 'error',\n `File system store unhandled error: ${ensureError(error)}`\n );\n }\n return await params.callback({ type: 'InternalError' });\n } finally {\n await file?.close().catch(() => {\n // already closed\n });\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fs-store.js","sources":["../src/fs-store.ts"],"sourcesContent":["import type { Readable } from 'node:stream';\nimport { stat, open, type FileHandle } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { PFrameInternal } from '@milaboratories/pl-model-middle-layer';\nimport { ensureError, isAbortError } from '@milaboratories/pl-model-common';\n\n/** Object store for serving files from a local directory */\nexport class FileSystemStore extends PFrameInternal.BaseObjectStore {\n private readonly rootDir: string;\n\n private constructor(options: PFrameInternal.FsStoreOptions) {\n super(options);\n\n this.rootDir = options.rootDir;\n }\n\n static async init(\n options: PFrameInternal.FsStoreOptions\n ): Promise<FileSystemStore> {\n const resolvedRootDir = resolve(options.rootDir);\n\n const rootStats = await stat(resolvedRootDir).catch(() => {\n throw new Error(\n `File system store root directory does not exist: ${resolvedRootDir}`\n );\n });\n if (!rootStats.isDirectory()) {\n throw new Error(\n `File system store root path is not a directory: ${resolvedRootDir}`\n );\n }\n\n return new FileSystemStore({\n ...options,\n rootDir: resolvedRootDir\n });\n }\n\n override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n }\n ): Promise<void> {\n let file: FileHandle | undefined;\n const respond = async (response: PFrameInternal.ObjectStoreResponse) => {\n try {\n await params\n .callback(response)\n .finally(async () => await file?.close());\n } catch (error: unknown) {\n this.logger(\n 'warn',\n `File system store received unexpected rejection from callback: ${ensureError(error)}`\n );\n }\n };\n\n try {\n try {\n const path = join(this.rootDir, filename);\n file = await open(path, 'r');\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to open file ${filename}: ${ensureError(error)}`\n );\n return await respond({ type: 'NotFound' });\n }\n params.signal.throwIfAborted();\n\n let size: number;\n try {\n ({ size } = await file.stat());\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to get size of file ${filename}: ${ensureError(error)}`\n );\n return await respond({ type: 'InternalError' });\n }\n params.signal.throwIfAborted();\n\n const range = this.translate(size, params.range);\n if (!range) {\n return await respond({ type: 'RangeNotSatisfiable', size });\n }\n\n if (params.method === 'HEAD') {\n return await respond({ type: 'Ok', size, range });\n }\n\n let data: Readable;\n try {\n data = file.createReadStream({\n start: range.start,\n end: range.end,\n autoClose: false\n });\n this.logger(\n 'info',\n `File system store created read stream for ${filename}[${range.start}..=${range.end}]`\n );\n } catch (error: unknown) {\n this.logger(\n 'error',\n `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${ensureError(error)}`\n );\n return await respond({ type: 'InternalError' });\n }\n\n return await respond({ type: 'Ok', size, range, data });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n this.logger(\n 'warn',\n `File system store unhandled error: ${ensureError(error)}`\n );\n }\n return await respond({ type: 'InternalError' });\n }\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "proxy",
9
9
  "server"
10
10
  ],
11
- "version": "1.0.69",
11
+ "version": "1.0.71",
12
12
  "type": "module",
13
13
  "types": "./dist/index.d.ts",
14
14
  "main": "./dist/index.js",
@@ -28,9 +28,9 @@
28
28
  "bin/**/*"
29
29
  ],
30
30
  "dependencies": {
31
- "@milaboratories/helpers": "1.6.22",
32
- "@milaboratories/pl-model-common": "1.19.14",
33
- "@milaboratories/pl-model-middle-layer": "1.8.21",
31
+ "@milaboratories/helpers": "1.7.0",
32
+ "@milaboratories/pl-model-common": "1.19.16",
33
+ "@milaboratories/pl-model-middle-layer": "1.8.23",
34
34
  "commander": "^14.0.0",
35
35
  "selfsigned": "^3.0.1"
36
36
  },
@@ -45,7 +45,7 @@
45
45
  "tslib": "^2.8.1",
46
46
  "typescript": "^5.9.2",
47
47
  "undici": "^7.15.0",
48
- "vite": "^7.1.3",
48
+ "vite": "^7.1.4",
49
49
  "vitest": "^3.2.4"
50
50
  },
51
51
  "license": "UNLICENSED",
package/src/fs-store.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import type { Readable } from 'node:stream';
2
- import { createReadStream } from 'node:fs';
3
2
  import { stat, open, type FileHandle } from 'node:fs/promises';
4
3
  import { join, resolve } from 'node:path';
5
4
  import { PFrameInternal } from '@milaboratories/pl-model-middle-layer';
6
5
  import { ensureError, isAbortError } from '@milaboratories/pl-model-common';
7
6
 
8
7
  /** Object store for serving files from a local directory */
9
- export class FileSystemStore extends PFrameInternal.ObjectStore {
8
+ export class FileSystemStore extends PFrameInternal.BaseObjectStore {
10
9
  private readonly rootDir: string;
11
10
 
12
11
  private constructor(options: PFrameInternal.FsStoreOptions) {
@@ -47,6 +46,19 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
47
46
  }
48
47
  ): Promise<void> {
49
48
  let file: FileHandle | undefined;
49
+ const respond = async (response: PFrameInternal.ObjectStoreResponse) => {
50
+ try {
51
+ await params
52
+ .callback(response)
53
+ .finally(async () => await file?.close());
54
+ } catch (error: unknown) {
55
+ this.logger(
56
+ 'warn',
57
+ `File system store received unexpected rejection from callback: ${ensureError(error)}`
58
+ );
59
+ }
60
+ };
61
+
50
62
  try {
51
63
  try {
52
64
  const path = join(this.rootDir, filename);
@@ -56,7 +68,7 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
56
68
  'error',
57
69
  `File system store failed to open file ${filename}: ${ensureError(error)}`
58
70
  );
59
- return await params.callback({ type: 'NotFound' });
71
+ return await respond({ type: 'NotFound' });
60
72
  }
61
73
  params.signal.throwIfAborted();
62
74
 
@@ -68,26 +80,25 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
68
80
  'error',
69
81
  `File system store failed to get size of file ${filename}: ${ensureError(error)}`
70
82
  );
71
- return await params.callback({ type: 'InternalError' });
83
+ return await respond({ type: 'InternalError' });
72
84
  }
73
85
  params.signal.throwIfAborted();
74
86
 
75
87
  const range = this.translate(size, params.range);
76
88
  if (!range) {
77
- return await params.callback({ type: 'RangeNotSatisfiable', size });
89
+ return await respond({ type: 'RangeNotSatisfiable', size });
78
90
  }
79
91
 
80
92
  if (params.method === 'HEAD') {
81
- return await params.callback({ type: 'Ok', size, range });
93
+ return await respond({ type: 'Ok', size, range });
82
94
  }
83
95
 
84
96
  let data: Readable;
85
97
  try {
86
- data = createReadStream('ignored', {
87
- fd: file.fd,
98
+ data = file.createReadStream({
88
99
  start: range.start,
89
100
  end: range.end,
90
- signal: params.signal
101
+ autoClose: false
91
102
  });
92
103
  this.logger(
93
104
  'info',
@@ -98,31 +109,18 @@ export class FileSystemStore extends PFrameInternal.ObjectStore {
98
109
  'error',
99
110
  `File system store failed to create read stream for ${filename}[${range.start}..=${range.end}]: ${ensureError(error)}`
100
111
  );
101
- return await params.callback({ type: 'InternalError' });
112
+ return await respond({ type: 'InternalError' });
102
113
  }
103
114
 
104
- try {
105
- return await params.callback({ type: 'Ok', size, range, data });
106
- } catch (error: unknown) {
107
- if (!isAbortError(error)) {
108
- this.logger(
109
- 'error',
110
- `File system store received unexpected rejection from callback: ${ensureError(error)}`
111
- );
112
- }
113
- }
115
+ return await respond({ type: 'Ok', size, range, data });
114
116
  } catch (error: unknown) {
115
117
  if (!isAbortError(error)) {
116
118
  this.logger(
117
- 'error',
119
+ 'warn',
118
120
  `File system store unhandled error: ${ensureError(error)}`
119
121
  );
120
122
  }
121
- return await params.callback({ type: 'InternalError' });
122
- } finally {
123
- await file?.close().catch(() => {
124
- // already closed
125
- });
123
+ return await respond({ type: 'InternalError' });
126
124
  }
127
125
  }
128
126
  }