@heripo/pdf-parser 0.1.24 → 0.1.26

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/index.d.cts CHANGED
@@ -130,6 +130,7 @@ declare class PDFParser {
130
130
  private readonly enableImagePdfFallback;
131
131
  private client;
132
132
  private environment?;
133
+ private recoveryPromise;
133
134
  constructor(options: Options);
134
135
  init(): Promise<void>;
135
136
  /**
@@ -137,6 +138,7 @@ declare class PDFParser {
137
138
  * This typically indicates the Docling server has crashed.
138
139
  */
139
140
  private isConnectionRefusedError;
141
+ private canRecoverLocalServer;
140
142
  /**
141
143
  * Restart the Docling server after it has crashed.
142
144
  * This kills any existing process on the port, starts a new server,
@@ -146,6 +148,9 @@ declare class PDFParser {
146
148
  * which guarantees this.port is defined.
147
149
  */
148
150
  private restartServer;
151
+ private recoverServer;
152
+ isReady(): Promise<boolean>;
153
+ ensureReady(): Promise<void>;
149
154
  private waitForServerReady;
150
155
  parse(url: string, reportId: string, onComplete: ConversionCompleteCallback, cleanupAfterCallback: boolean, options: PDFConvertOptions, abortSignal?: AbortSignal): Promise<TokenUsageReport | null>;
151
156
  /**
@@ -174,7 +179,7 @@ declare class ImagePdfFallbackError extends Error {
174
179
 
175
180
  /**
176
181
  * Error thrown when the uploaded PDF does not appear to be
177
- * a Korean archaeological investigation report.
182
+ * an archaeological investigation report.
178
183
  */
179
184
  declare class InvalidDocumentTypeError extends Error {
180
185
  readonly reason: string;
package/dist/index.d.ts CHANGED
@@ -130,6 +130,7 @@ declare class PDFParser {
130
130
  private readonly enableImagePdfFallback;
131
131
  private client;
132
132
  private environment?;
133
+ private recoveryPromise;
133
134
  constructor(options: Options);
134
135
  init(): Promise<void>;
135
136
  /**
@@ -137,6 +138,7 @@ declare class PDFParser {
137
138
  * This typically indicates the Docling server has crashed.
138
139
  */
139
140
  private isConnectionRefusedError;
141
+ private canRecoverLocalServer;
140
142
  /**
141
143
  * Restart the Docling server after it has crashed.
142
144
  * This kills any existing process on the port, starts a new server,
@@ -146,6 +148,9 @@ declare class PDFParser {
146
148
  * which guarantees this.port is defined.
147
149
  */
148
150
  private restartServer;
151
+ private recoverServer;
152
+ isReady(): Promise<boolean>;
153
+ ensureReady(): Promise<void>;
149
154
  private waitForServerReady;
150
155
  parse(url: string, reportId: string, onComplete: ConversionCompleteCallback, cleanupAfterCallback: boolean, options: PDFConvertOptions, abortSignal?: AbortSignal): Promise<TokenUsageReport | null>;
151
156
  /**
@@ -174,7 +179,7 @@ declare class ImagePdfFallbackError extends Error {
174
179
 
175
180
  /**
176
181
  * Error thrown when the uploaded PDF does not appear to be
177
- * a Korean archaeological investigation report.
182
+ * an archaeological investigation report.
178
183
  */
179
184
  declare class InvalidDocumentTypeError extends Error {
180
185
  readonly reason: string;
package/dist/index.js CHANGED
@@ -703,14 +703,32 @@ function validatePythonVersion(version, context = "system") {
703
703
  }
704
704
 
705
705
  // src/environment/python-environment.ts
706
+ var DOCLING_SERVE_PACKAGE = "docling-serve==1.16.1";
706
707
  var DOCLING_RUNTIME_PACKAGES = [
707
- "docling-serve==1.16.1",
708
- "docling-jobkit==1.17.0",
709
- "docling==2.90.0",
710
- "docling-core==2.74.0",
708
+ "docling-jobkit==1.16.0",
709
+ "docling==2.88.0",
710
+ "docling-core==2.73.0",
711
711
  "docling-ibm-models==3.13.0",
712
712
  "docling-parse==5.9.0"
713
713
  ];
714
+ var DOCLING_SERVE_RUNTIME_PACKAGES = [
715
+ "fastapi[standard]<0.130.0",
716
+ "httpx~=0.28",
717
+ "pydantic~=2.10",
718
+ "pydantic-settings~=2.4",
719
+ "python-multipart<0.1.0,>=0.0.14",
720
+ "typer~=0.12",
721
+ "uvicorn[standard]<1.0.0,>=0.29.0",
722
+ "websockets<17.0,>=14.0",
723
+ "scalar-fastapi>=1.0.3",
724
+ "docling-mcp>=1.0.0",
725
+ "opentelemetry-api==1.36.0",
726
+ "opentelemetry-sdk==1.36.0",
727
+ "opentelemetry-exporter-otlp==1.36.0",
728
+ "opentelemetry-instrumentation-fastapi==0.57b0",
729
+ "opentelemetry-exporter-prometheus==0.57b0",
730
+ "prometheus-client>=0.21.0"
731
+ ];
714
732
  var PythonEnvironment = class {
715
733
  constructor(logger, venvPath) {
716
734
  this.logger = logger;
@@ -722,6 +740,8 @@ var PythonEnvironment = class {
722
740
  await this.upgradePip();
723
741
  await this.installSetuptools();
724
742
  await this.installPyArrow();
743
+ await this.installDoclingRuntimePackages();
744
+ await this.installDoclingServeRuntimePackages();
725
745
  await this.installDoclingServe();
726
746
  }
727
747
  async checkPythonVersion() {
@@ -815,13 +835,31 @@ var PythonEnvironment = class {
815
835
  throw new Error(`Failed to install pyarrow. Exit code: ${result.code}`);
816
836
  }
817
837
  }
818
- async installDoclingServe() {
838
+ async installDoclingRuntimePackages() {
819
839
  const pipPath = join2(this.venvPath, "bin", "pip");
820
840
  const result = await spawnAsync(pipPath, [
821
841
  "install",
822
842
  "--upgrade",
823
843
  ...DOCLING_RUNTIME_PACKAGES
824
844
  ]);
845
+ if (result.code !== 0) {
846
+ this.logger.error(
847
+ "[DoclingEnvironment] Failed to install docling runtime packages:",
848
+ result.stderr
849
+ );
850
+ throw new Error(
851
+ `Failed to install docling runtime packages. Exit code: ${result.code}`
852
+ );
853
+ }
854
+ }
855
+ async installDoclingServe() {
856
+ const pipPath = join2(this.venvPath, "bin", "pip");
857
+ const result = await spawnAsync(pipPath, [
858
+ "install",
859
+ "--upgrade",
860
+ "--no-deps",
861
+ DOCLING_SERVE_PACKAGE
862
+ ]);
825
863
  if (result.code !== 0) {
826
864
  this.logger.error(
827
865
  "[DoclingEnvironment] Failed to install docling-serve:",
@@ -832,6 +870,23 @@ var PythonEnvironment = class {
832
870
  );
833
871
  }
834
872
  }
873
+ async installDoclingServeRuntimePackages() {
874
+ const pipPath = join2(this.venvPath, "bin", "pip");
875
+ const result = await spawnAsync(pipPath, [
876
+ "install",
877
+ "--upgrade",
878
+ ...DOCLING_SERVE_RUNTIME_PACKAGES
879
+ ]);
880
+ if (result.code !== 0) {
881
+ this.logger.error(
882
+ "[DoclingEnvironment] Failed to install docling-serve runtime packages:",
883
+ result.stderr
884
+ );
885
+ throw new Error(
886
+ `Failed to install docling-serve runtime packages. Exit code: ${result.code}`
887
+ );
888
+ }
889
+ }
835
890
  };
836
891
 
837
892
  // src/environment/docling-environment.ts
@@ -1045,7 +1100,7 @@ import { z } from "zod";
1045
1100
  var InvalidDocumentTypeError = class extends Error {
1046
1101
  constructor(reason) {
1047
1102
  super(
1048
- `The uploaded PDF does not appear to be a Korean archaeological investigation report. Reason: ${reason}`
1103
+ `The uploaded PDF does not appear to be an archaeological investigation report. Reason: ${reason}`
1049
1104
  );
1050
1105
  this.reason = reason;
1051
1106
  }
@@ -3550,7 +3605,7 @@ var PDFConverter = class {
3550
3605
  }
3551
3606
  documentTypeValidated = false;
3552
3607
  /**
3553
- * Validate that the PDF is a Korean archaeological investigation report.
3608
+ * Validate that the PDF is an archaeological investigation report.
3554
3609
  * Skipped when no documentValidationModel is configured or for non-local URLs.
3555
3610
  * Only runs once per converter instance (flag prevents duplicate checks on recursive calls).
3556
3611
  */
@@ -3839,6 +3894,7 @@ var PDFParser = class {
3839
3894
  enableImagePdfFallback;
3840
3895
  client = null;
3841
3896
  environment;
3897
+ recoveryPromise = null;
3842
3898
  constructor(options) {
3843
3899
  const {
3844
3900
  logger,
@@ -3923,15 +3979,30 @@ var PDFParser = class {
3923
3979
  */
3924
3980
  isConnectionRefusedError(error) {
3925
3981
  if (error instanceof Error) {
3982
+ const errorWithMetadata = error;
3926
3983
  if (error.message.includes("ECONNREFUSED")) {
3927
3984
  return true;
3928
3985
  }
3929
- if (error.cause instanceof Error && error.cause.message.includes("ECONNREFUSED")) {
3986
+ if (errorWithMetadata.code === "ECONNREFUSED") {
3930
3987
  return true;
3931
3988
  }
3989
+ return this.isConnectionRefusedError(errorWithMetadata.cause);
3990
+ }
3991
+ if (typeof error === "object" && error !== null) {
3992
+ const errorLike = error;
3993
+ if (errorLike.code === "ECONNREFUSED") {
3994
+ return true;
3995
+ }
3996
+ if (typeof errorLike.message === "string" && errorLike.message.includes("ECONNREFUSED")) {
3997
+ return true;
3998
+ }
3999
+ return this.isConnectionRefusedError(errorLike.cause);
3932
4000
  }
3933
4001
  return false;
3934
4002
  }
4003
+ canRecoverLocalServer() {
4004
+ return !this.baseUrl && this.port !== void 0;
4005
+ }
3935
4006
  /**
3936
4007
  * Restart the Docling server after it has crashed.
3937
4008
  * This kills any existing process on the port, starts a new server,
@@ -3961,6 +4032,46 @@ var PDFParser = class {
3961
4032
  await this.waitForServerReady();
3962
4033
  this.logger.info("[PDFParser] Server restarted successfully");
3963
4034
  }
4035
+ async recoverServer(message) {
4036
+ this.logger.warn(message);
4037
+ if (!this.recoveryPromise) {
4038
+ this.recoveryPromise = this.restartServer().finally(() => {
4039
+ this.recoveryPromise = null;
4040
+ });
4041
+ } else {
4042
+ this.logger.warn("[PDFParser] Server recovery already in progress...");
4043
+ }
4044
+ await this.recoveryPromise;
4045
+ }
4046
+ async isReady() {
4047
+ if (!this.client) {
4048
+ return false;
4049
+ }
4050
+ try {
4051
+ await this.client.health();
4052
+ return true;
4053
+ } catch {
4054
+ return false;
4055
+ }
4056
+ }
4057
+ async ensureReady() {
4058
+ if (!this.client) {
4059
+ throw new Error(
4060
+ "PDFParser is not initialized. Call init() before checking readiness"
4061
+ );
4062
+ }
4063
+ try {
4064
+ await this.client.health();
4065
+ } catch (error) {
4066
+ if (this.canRecoverLocalServer()) {
4067
+ await this.recoverServer(
4068
+ "[PDFParser] Health check failed, attempting server recovery..."
4069
+ );
4070
+ return;
4071
+ }
4072
+ throw error;
4073
+ }
4074
+ }
3964
4075
  async waitForServerReady() {
3965
4076
  const maxAttempts = PDF_PARSER.MAX_HEALTH_CHECK_ATTEMPTS;
3966
4077
  const checkInterval = PDF_PARSER.HEALTH_CHECK_INTERVAL_MS;
@@ -4044,18 +4155,16 @@ var PDFParser = class {
4044
4155
  * Recovery is only attempted in local server mode (no baseUrl, port defined).
4045
4156
  */
4046
4157
  async executeWithRecovery(fn, abortSignal) {
4047
- const canRecover = !this.baseUrl && this.port !== void 0;
4048
4158
  const maxAttempts = PDF_PARSER.MAX_SERVER_RECOVERY_ATTEMPTS;
4049
4159
  const tryExecute = async (attempt) => {
4050
4160
  try {
4051
4161
  return await fn();
4052
4162
  } catch (error) {
4053
4163
  if (abortSignal?.aborted) throw error;
4054
- if (canRecover && this.isConnectionRefusedError(error) && attempt < maxAttempts) {
4055
- this.logger.warn(
4164
+ if (this.canRecoverLocalServer() && this.isConnectionRefusedError(error) && attempt < maxAttempts) {
4165
+ await this.recoverServer(
4056
4166
  "[PDFParser] Connection refused, attempting server recovery..."
4057
4167
  );
4058
- await this.restartServer();
4059
4168
  return tryExecute(attempt + 1);
4060
4169
  }
4061
4170
  throw error;