@open-kingdom/shared-backend-integration-test-doubles 0.0.2-17 → 0.0.2-18

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.
Files changed (99) hide show
  1. package/README.md +75 -5
  2. package/dist/gcs/gcs-error-mode.d.ts +161 -0
  3. package/dist/gcs/gcs-error-mode.d.ts.map +1 -0
  4. package/dist/gcs/gcs-error-mode.js +256 -0
  5. package/dist/gcs/gcs-error-mode.js.map +1 -0
  6. package/dist/gcs/gcs-error-simulation.interceptor.d.ts +21 -0
  7. package/dist/gcs/gcs-error-simulation.interceptor.d.ts.map +1 -0
  8. package/dist/gcs/gcs-error-simulation.interceptor.js +40 -0
  9. package/dist/gcs/gcs-error-simulation.interceptor.js.map +1 -0
  10. package/dist/gcs/gcs-twin.d.ts +1 -0
  11. package/dist/gcs/gcs-twin.d.ts.map +1 -1
  12. package/dist/gcs/gcs-twin.js +19 -1
  13. package/dist/gcs/gcs-twin.js.map +1 -1
  14. package/dist/gcs/index.d.ts +3 -0
  15. package/dist/gcs/index.d.ts.map +1 -1
  16. package/dist/gcs/index.js +2 -0
  17. package/dist/gcs/index.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +4 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/interceptor/http-helper.d.ts +37 -0
  23. package/dist/interceptor/http-helper.d.ts.map +1 -0
  24. package/dist/interceptor/http-helper.js +109 -0
  25. package/dist/interceptor/http-helper.js.map +1 -0
  26. package/dist/interceptor/index.d.ts +4 -0
  27. package/dist/interceptor/index.d.ts.map +1 -0
  28. package/dist/interceptor/index.js +5 -0
  29. package/dist/interceptor/index.js.map +1 -0
  30. package/dist/interceptor/node-interceptor.d.ts +40 -0
  31. package/dist/interceptor/node-interceptor.d.ts.map +1 -0
  32. package/dist/interceptor/node-interceptor.js +188 -0
  33. package/dist/interceptor/node-interceptor.js.map +1 -0
  34. package/dist/interceptor/routing-table.config.d.ts +3 -0
  35. package/dist/interceptor/routing-table.config.d.ts.map +1 -0
  36. package/dist/interceptor/routing-table.config.js +25 -0
  37. package/dist/interceptor/routing-table.config.js.map +1 -0
  38. package/dist/interceptor/routing-table.d.ts +15 -0
  39. package/dist/interceptor/routing-table.d.ts.map +1 -0
  40. package/dist/interceptor/routing-table.js +44 -0
  41. package/dist/interceptor/routing-table.js.map +1 -0
  42. package/dist/shared/constants.d.ts +4 -0
  43. package/dist/shared/constants.d.ts.map +1 -1
  44. package/dist/shared/constants.js +3 -1
  45. package/dist/shared/constants.js.map +1 -1
  46. package/dist/youtube/index.d.ts +15 -0
  47. package/dist/youtube/index.d.ts.map +1 -0
  48. package/dist/youtube/index.js +11 -0
  49. package/dist/youtube/index.js.map +1 -0
  50. package/dist/youtube/msw-handlers.d.ts +40 -0
  51. package/dist/youtube/msw-handlers.d.ts.map +1 -0
  52. package/dist/youtube/msw-handlers.js +45 -0
  53. package/dist/youtube/msw-handlers.js.map +1 -0
  54. package/dist/youtube/player-shim.d.ts +3 -0
  55. package/dist/youtube/player-shim.d.ts.map +1 -0
  56. package/dist/youtube/player-shim.js +296 -0
  57. package/dist/youtube/player-shim.js.map +1 -0
  58. package/dist/youtube/search-fixtures.d.ts +29 -0
  59. package/dist/youtube/search-fixtures.d.ts.map +1 -0
  60. package/dist/youtube/search-fixtures.js +392 -0
  61. package/dist/youtube/search-fixtures.js.map +1 -0
  62. package/dist/youtube/search-response.d.ts +51 -0
  63. package/dist/youtube/search-response.d.ts.map +1 -0
  64. package/dist/youtube/search-response.js +53 -0
  65. package/dist/youtube/search-response.js.map +1 -0
  66. package/dist/youtube/youtube-error-mode.d.ts +36 -0
  67. package/dist/youtube/youtube-error-mode.d.ts.map +1 -0
  68. package/dist/youtube/youtube-error-mode.js +87 -0
  69. package/dist/youtube/youtube-error-mode.js.map +1 -0
  70. package/dist/youtube/youtube-twin.config.d.ts +13 -0
  71. package/dist/youtube/youtube-twin.config.d.ts.map +1 -0
  72. package/dist/youtube/youtube-twin.config.js +32 -0
  73. package/dist/youtube/youtube-twin.config.js.map +1 -0
  74. package/dist/youtube/youtube-twin.d.ts +14 -0
  75. package/dist/youtube/youtube-twin.d.ts.map +1 -0
  76. package/dist/youtube/youtube-twin.js +162 -0
  77. package/dist/youtube/youtube-twin.js.map +1 -0
  78. package/package.json +15 -2
  79. package/test-assets/youtube/sample.mp4 +0 -0
  80. package/test-assets/youtube/thumbnails/thumbnail-01.jpg +0 -0
  81. package/test-assets/youtube/thumbnails/thumbnail-02.jpg +0 -0
  82. package/test-assets/youtube/thumbnails/thumbnail-03.jpg +0 -0
  83. package/test-assets/youtube/thumbnails/thumbnail-04.jpg +0 -0
  84. package/test-assets/youtube/thumbnails/thumbnail-05.jpg +0 -0
  85. package/test-assets/youtube/thumbnails/thumbnail-06.jpg +0 -0
  86. package/test-assets/youtube/thumbnails/thumbnail-07.jpg +0 -0
  87. package/test-assets/youtube/thumbnails/thumbnail-08.jpg +0 -0
  88. package/test-assets/youtube/thumbnails/thumbnail-09.jpg +0 -0
  89. package/test-assets/youtube/thumbnails/thumbnail-10.jpg +0 -0
  90. package/test-assets/youtube/thumbnails/thumbnail-11.jpg +0 -0
  91. package/test-assets/youtube/thumbnails/thumbnail-12.jpg +0 -0
  92. package/test-assets/youtube/thumbnails/thumbnail-13.jpg +0 -0
  93. package/test-assets/youtube/thumbnails/thumbnail-14.jpg +0 -0
  94. package/test-assets/youtube/thumbnails/thumbnail-15.jpg +0 -0
  95. package/test-assets/youtube/thumbnails/thumbnail-16.jpg +0 -0
  96. package/test-assets/youtube/thumbnails/thumbnail-17.jpg +0 -0
  97. package/test-assets/youtube/thumbnails/thumbnail-18.jpg +0 -0
  98. package/test-assets/youtube/thumbnails/thumbnail-19.jpg +0 -0
  99. package/test-assets/youtube/thumbnails/thumbnail-20.jpg +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"gcs-twin.d.ts","sourceRoot":"","sources":["../../src/gcs/gcs-twin.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAmB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAErE;;;;;GAKG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;;;OAKG;gBACS,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM;IAM/D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;YAWrB,sBAAsB;YAkBtB,iBAAiB;YAYjB,SAAS;YAwBT,eAAe;YAwBf,cAAc;YAOd,cAAc;IAqB5B;;OAEG;YACW,aAAa;IA+B3B;;OAEG;YACW,gBAAgB;CAkB/B"}
1
+ {"version":3,"file":"gcs-twin.d.ts","sourceRoot":"","sources":["../../src/gcs/gcs-twin.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAmB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAErE;;;;;GAKG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,iBAAiB,CAAS;IAElC;;;;;OAKG;gBACS,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM;IAM/D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;YAcrB,sBAAsB;YAkBtB,iBAAiB;YAYjB,SAAS;YAwBT,eAAe;YAwBf,cAAc;YAOd,cAAc;IAqB5B;;OAEG;YACW,aAAa;IA+B3B;;OAEG;YACW,gBAAgB;CAkB/B"}
@@ -13,6 +13,13 @@ import { createGcsConfig } from '../shared/config.js';
13
13
  * @throws {Error} If the container fails to start.
14
14
  */ async start() {
15
15
  console.log(`Starting GCS twin on port ${this.config.port}...`);
16
+ try {
17
+ await this.docker.ping();
18
+ } catch (_err) {
19
+ console.warn(`[GcsTwin] Docker is not available. Skipping GCS emulator container startup. GCS emulator will be offline.`);
20
+ this.dockerUnavailable = true;
21
+ return;
22
+ }
16
23
  await this.ensureImageExists();
17
24
  await this.ensureContainerRemoved();
18
25
  await this.createContainer();
@@ -25,6 +32,10 @@ import { createGcsConfig } from '../shared/config.js';
25
32
  /**
26
33
  * Stop and remove the Docker container.
27
34
  */ async stop() {
35
+ if (this.dockerUnavailable) {
36
+ this.dockerUnavailable = false;
37
+ return;
38
+ }
28
39
  if (!this.container) {
29
40
  return;
30
41
  }
@@ -42,6 +53,9 @@ import { createGcsConfig } from '../shared/config.js';
42
53
  /**
43
54
  * Reset all data: delete buckets then re‑create them.
44
55
  */ async reset() {
56
+ if (this.dockerUnavailable) {
57
+ return;
58
+ }
45
59
  console.log('Resetting GCS twin...');
46
60
  for (const bucket of this.config.buckets){
47
61
  try {
@@ -63,6 +77,9 @@ import { createGcsConfig } from '../shared/config.js';
63
77
  /**
64
78
  * Check if the twin is running and healthy.
65
79
  */ async isHealthy() {
80
+ if (this.dockerUnavailable) {
81
+ return false;
82
+ }
66
83
  try {
67
84
  const response = await fetch(`${this.config.externalUrl}/storage/v1/b`);
68
85
  return response.ok;
@@ -104,7 +121,7 @@ import { createGcsConfig } from '../shared/config.js';
104
121
  return new Promise((resolve, reject)=>{
105
122
  this.docker.pull(imageName, {}, (err, stream)=>{
106
123
  if (err || !stream) return reject(err != null ? err : new Error('No stream returned from docker.pull'));
107
- this.docker.modem.followProgress(stream, (err, output)=>err ? reject(err) : resolve(output), (event)=>{
124
+ this.docker.modem.followProgress(stream, (err, _output)=>err ? reject(err) : resolve(), (event)=>{
108
125
  if (event.status === 'Downloading' || event.status === 'Extracting') {
109
126
  // Optionally log progress here
110
127
  }
@@ -210,6 +227,7 @@ import { createGcsConfig } from '../shared/config.js';
210
227
  * @param docker Optional Docker instance (for testing).
211
228
  */ constructor(overrides, docker){
212
229
  this.container = null;
230
+ this.dockerUnavailable = false;
213
231
  this.config = createGcsConfig(overrides);
214
232
  this.docker = docker != null ? docker : new Docker();
215
233
  this.containerName = `itd-gcs-twin-${this.config.port}`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/gcs/gcs-twin.ts"],"sourcesContent":["import Docker from 'dockerode';\nimport { createGcsConfig, GcsTwinConfig } from '../shared/config.js';\n\n/**\n * GCS twin lifecycle manager.\n *\n * Starts/stops a fake‑gcs‑server Docker container, creates buckets,\n * and sets the GCS_EMULATOR_URL environment variable.\n */\nexport class GcsTwin {\n private readonly config: GcsTwinConfig;\n private readonly docker: Docker;\n private container: Docker.Container | null = null;\n private readonly containerName: string;\n\n /**\n * Create a new GCS twin.\n *\n * @param overrides Optional configuration overrides (merged with defaults and env vars).\n * @param docker Optional Docker instance (for testing).\n */\n constructor(overrides?: Partial<GcsTwinConfig>, docker?: Docker) {\n this.config = createGcsConfig(overrides);\n this.docker = docker ?? new Docker();\n this.containerName = `itd-gcs-twin-${this.config.port}`;\n }\n\n /**\n * Start the Docker container and create buckets.\n *\n * @throws {Error} If the container fails to start.\n */\n async start(): Promise<void> {\n console.log(`Starting GCS twin on port ${this.config.port}...`);\n await this.ensureImageExists();\n await this.ensureContainerRemoved();\n await this.createContainer();\n await this.startContainer();\n await this.waitForHealthy();\n await this.createBuckets();\n process.env.GCS_EMULATOR_URL = this.config.externalUrl;\n console.log(`GCS twin ready at ${this.config.externalUrl}`);\n }\n\n /**\n * Stop and remove the Docker container.\n */\n async stop(): Promise<void> {\n if (!this.container) {\n return;\n }\n console.log(`Stopping GCS twin (${this.containerName})...`);\n try {\n await this.container.stop();\n await this.container.remove();\n } catch (err) {\n console.warn('Error stopping container:', err);\n }\n this.container = null;\n delete process.env.GCS_EMULATOR_URL;\n console.log('GCS twin stopped');\n }\n\n /**\n * Reset all data: delete buckets then re‑create them.\n */\n async reset(): Promise<void> {\n console.log('Resetting GCS twin...');\n for (const bucket of this.config.buckets) {\n try {\n await fetch(`${this.config.externalUrl}/storage/v1/b/${bucket.name}`, {\n method: 'DELETE',\n });\n } catch (_err) {\n // Bucket may not exist; ignore\n }\n }\n await this.createBuckets();\n console.log('GCS twin reset complete');\n }\n\n /**\n * Get the emulator host URL for SDK configuration.\n */\n getEmulatorHost(): string {\n return this.config.externalUrl;\n }\n\n /**\n * Check if the twin is running and healthy.\n */\n async isHealthy(): Promise<boolean> {\n try {\n const response = await fetch(`${this.config.externalUrl}/storage/v1/b`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n // --- internal helpers ---\n\n private async ensureContainerRemoved(): Promise<void> {\n try {\n const existing = this.docker.getContainer(this.containerName);\n const info = await existing.inspect().catch(() => null);\n if (info) {\n console.log(`Removing existing container ${this.containerName}...`);\n await existing.stop().catch(() => {\n /* ignore */\n });\n await existing.remove().catch(() => {\n /* ignore */\n });\n }\n } catch (_err) {\n // No existing container\n }\n }\n\n private async ensureImageExists(): Promise<void> {\n const imageName = 'fsouza/fake-gcs-server';\n const images = await this.docker.listImages({\n filters: JSON.stringify({ reference: [imageName] }),\n });\n\n if (images.length === 0) {\n console.log(`Image ${imageName} not found. Pulling...`);\n await this.pullImage(imageName);\n }\n }\n\n private async pullImage(imageName: string): Promise<void> {\n return new Promise((resolve, reject) => {\n this.docker.pull(imageName, {}, (err, stream) => {\n if (err || !stream)\n return reject(\n err ?? new Error('No stream returned from docker.pull')\n );\n this.docker.modem.followProgress(\n stream,\n (err: Error | null, output: any) =>\n err ? reject(err) : resolve(output),\n (event: any) => {\n if (\n event.status === 'Downloading' ||\n event.status === 'Extracting'\n ) {\n // Optionally log progress here\n }\n }\n );\n });\n });\n }\n\n private async createContainer(): Promise<void> {\n const container = await this.docker.createContainer({\n Image: 'fsouza/fake-gcs-server',\n name: this.containerName,\n HostConfig: {\n PortBindings: {\n '4443/tcp': [{ HostPort: `${this.config.port}` }],\n },\n },\n ExposedPorts: {\n '4443/tcp': {},\n },\n Cmd: [\n '-scheme',\n 'http',\n '-port',\n '4443',\n '-external-url',\n this.config.externalUrl,\n ],\n });\n this.container = container;\n }\n\n private async startContainer(): Promise<void> {\n if (!this.container) {\n throw new Error('Container not created');\n }\n await this.container.start();\n }\n\n private async waitForHealthy(): Promise<void> {\n const maxAttempts = 30;\n const intervalMs = 500;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (await this.isHealthy()) {\n // Small delay after health check passes — the container may report\n // healthy before all HTTP methods are ready (common with fake-gcs-server).\n await new Promise((resolve) => setTimeout(resolve, 1000));\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n\n throw new Error(\n `GCS twin did not become healthy within ${\n (maxAttempts * intervalMs) / 1000\n }s`\n );\n }\n\n /**\n * Internal: create buckets. Errors are logged but not thrown.\n */\n private async createBuckets(): Promise<void> {\n console.log('Creating buckets...');\n for (const bucket of this.config.buckets) {\n try {\n const createRes = await this.fetchWithTimeout(\n `${this.config.externalUrl}/storage/v1/b`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: bucket.name }),\n },\n 10_000\n );\n if (!createRes.ok && createRes.status !== 409) {\n console.warn(\n `Warning: failed to create bucket ${bucket.name}: ${createRes.status} ${createRes.statusText}`\n );\n } else {\n console.log(` Created bucket ${bucket.name}`);\n }\n } catch (err) {\n console.warn(\n `Warning: failed to create bucket ${bucket.name}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n console.log('Bucket creation complete');\n }\n\n /**\n * Fetch with a timeout. Aborts the request if it exceeds the given duration.\n */\n private async fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number\n ): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (err) {\n if (controller.signal.aborted) {\n throw new Error(`Request to ${url} timed out after ${timeoutMs}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n"],"names":["Docker","createGcsConfig","GcsTwin","start","console","log","config","port","ensureImageExists","ensureContainerRemoved","createContainer","startContainer","waitForHealthy","createBuckets","process","env","GCS_EMULATOR_URL","externalUrl","stop","container","containerName","remove","err","warn","reset","bucket","buckets","fetch","name","method","_err","getEmulatorHost","isHealthy","response","ok","existing","docker","getContainer","info","inspect","catch","imageName","images","listImages","filters","JSON","stringify","reference","length","pullImage","Promise","resolve","reject","pull","stream","Error","modem","followProgress","output","event","status","Image","HostConfig","PortBindings","HostPort","ExposedPorts","Cmd","maxAttempts","intervalMs","attempt","setTimeout","createRes","fetchWithTimeout","headers","body","statusText","message","String","url","init","timeoutMs","controller","AbortController","timer","abort","signal","aborted","clearTimeout","constructor","overrides"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
1
+ {"version":3,"sources":["../../src/gcs/gcs-twin.ts"],"sourcesContent":["import Docker from 'dockerode';\nimport { createGcsConfig, GcsTwinConfig } from '../shared/config.js';\n\n/**\n * GCS twin lifecycle manager.\n *\n * Starts/stops a fake‑gcs‑server Docker container, creates buckets,\n * and sets the GCS_EMULATOR_URL environment variable.\n */\nexport class GcsTwin {\n private readonly config: GcsTwinConfig;\n private readonly docker: Docker;\n private container: Docker.Container | null = null;\n private readonly containerName: string;\n private dockerUnavailable = false;\n\n /**\n * Create a new GCS twin.\n *\n * @param overrides Optional configuration overrides (merged with defaults and env vars).\n * @param docker Optional Docker instance (for testing).\n */\n constructor(overrides?: Partial<GcsTwinConfig>, docker?: Docker) {\n this.config = createGcsConfig(overrides);\n this.docker = docker ?? new Docker();\n this.containerName = `itd-gcs-twin-${this.config.port}`;\n }\n\n /**\n * Start the Docker container and create buckets.\n *\n * @throws {Error} If the container fails to start.\n */\n async start(): Promise<void> {\n console.log(`Starting GCS twin on port ${this.config.port}...`);\n try {\n await this.docker.ping();\n } catch (_err) {\n console.warn(\n `[GcsTwin] Docker is not available. Skipping GCS emulator container startup. GCS emulator will be offline.`\n );\n this.dockerUnavailable = true;\n return;\n }\n\n await this.ensureImageExists();\n await this.ensureContainerRemoved();\n await this.createContainer();\n await this.startContainer();\n await this.waitForHealthy();\n await this.createBuckets();\n process.env.GCS_EMULATOR_URL = this.config.externalUrl;\n console.log(`GCS twin ready at ${this.config.externalUrl}`);\n }\n\n /**\n * Stop and remove the Docker container.\n */\n async stop(): Promise<void> {\n if (this.dockerUnavailable) {\n this.dockerUnavailable = false;\n return;\n }\n if (!this.container) {\n return;\n }\n console.log(`Stopping GCS twin (${this.containerName})...`);\n try {\n await this.container.stop();\n await this.container.remove();\n } catch (err) {\n console.warn('Error stopping container:', err);\n }\n this.container = null;\n delete process.env.GCS_EMULATOR_URL;\n console.log('GCS twin stopped');\n }\n\n /**\n * Reset all data: delete buckets then re‑create them.\n */\n async reset(): Promise<void> {\n if (this.dockerUnavailable) {\n return;\n }\n console.log('Resetting GCS twin...');\n for (const bucket of this.config.buckets) {\n try {\n await fetch(`${this.config.externalUrl}/storage/v1/b/${bucket.name}`, {\n method: 'DELETE',\n });\n } catch (_err) {\n // Bucket may not exist; ignore\n }\n }\n await this.createBuckets();\n console.log('GCS twin reset complete');\n }\n\n /**\n * Get the emulator host URL for SDK configuration.\n */\n getEmulatorHost(): string {\n return this.config.externalUrl;\n }\n\n /**\n * Check if the twin is running and healthy.\n */\n async isHealthy(): Promise<boolean> {\n if (this.dockerUnavailable) {\n return false;\n }\n try {\n const response = await fetch(`${this.config.externalUrl}/storage/v1/b`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n // --- internal helpers ---\n\n private async ensureContainerRemoved(): Promise<void> {\n try {\n const existing = this.docker.getContainer(this.containerName);\n const info = await existing.inspect().catch(() => null);\n if (info) {\n console.log(`Removing existing container ${this.containerName}...`);\n await existing.stop().catch(() => {\n /* ignore */\n });\n await existing.remove().catch(() => {\n /* ignore */\n });\n }\n } catch (_err) {\n // No existing container\n }\n }\n\n private async ensureImageExists(): Promise<void> {\n const imageName = 'fsouza/fake-gcs-server';\n const images = await this.docker.listImages({\n filters: JSON.stringify({ reference: [imageName] }),\n });\n\n if (images.length === 0) {\n console.log(`Image ${imageName} not found. Pulling...`);\n await this.pullImage(imageName);\n }\n }\n\n private async pullImage(imageName: string): Promise<void> {\n return new Promise((resolve, reject) => {\n this.docker.pull(imageName, {}, (err, stream) => {\n if (err || !stream)\n return reject(\n err ?? new Error('No stream returned from docker.pull')\n );\n this.docker.modem.followProgress(\n stream,\n (err: Error | null, _output: unknown) =>\n err ? reject(err) : resolve(),\n (event: { status?: string }) => {\n if (\n event.status === 'Downloading' ||\n event.status === 'Extracting'\n ) {\n // Optionally log progress here\n }\n }\n );\n });\n });\n }\n\n private async createContainer(): Promise<void> {\n const container = await this.docker.createContainer({\n Image: 'fsouza/fake-gcs-server',\n name: this.containerName,\n HostConfig: {\n PortBindings: {\n '4443/tcp': [{ HostPort: `${this.config.port}` }],\n },\n },\n ExposedPorts: {\n '4443/tcp': {},\n },\n Cmd: [\n '-scheme',\n 'http',\n '-port',\n '4443',\n '-external-url',\n this.config.externalUrl,\n ],\n });\n this.container = container;\n }\n\n private async startContainer(): Promise<void> {\n if (!this.container) {\n throw new Error('Container not created');\n }\n await this.container.start();\n }\n\n private async waitForHealthy(): Promise<void> {\n const maxAttempts = 30;\n const intervalMs = 500;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (await this.isHealthy()) {\n // Small delay after health check passes — the container may report\n // healthy before all HTTP methods are ready (common with fake-gcs-server).\n await new Promise((resolve) => setTimeout(resolve, 1000));\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n\n throw new Error(\n `GCS twin did not become healthy within ${\n (maxAttempts * intervalMs) / 1000\n }s`\n );\n }\n\n /**\n * Internal: create buckets. Errors are logged but not thrown.\n */\n private async createBuckets(): Promise<void> {\n console.log('Creating buckets...');\n for (const bucket of this.config.buckets) {\n try {\n const createRes = await this.fetchWithTimeout(\n `${this.config.externalUrl}/storage/v1/b`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: bucket.name }),\n },\n 10_000\n );\n if (!createRes.ok && createRes.status !== 409) {\n console.warn(\n `Warning: failed to create bucket ${bucket.name}: ${createRes.status} ${createRes.statusText}`\n );\n } else {\n console.log(` Created bucket ${bucket.name}`);\n }\n } catch (err) {\n console.warn(\n `Warning: failed to create bucket ${bucket.name}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n console.log('Bucket creation complete');\n }\n\n /**\n * Fetch with a timeout. Aborts the request if it exceeds the given duration.\n */\n private async fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number\n ): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (err) {\n if (controller.signal.aborted) {\n throw new Error(`Request to ${url} timed out after ${timeoutMs}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n"],"names":["Docker","createGcsConfig","GcsTwin","start","console","log","config","port","docker","ping","_err","warn","dockerUnavailable","ensureImageExists","ensureContainerRemoved","createContainer","startContainer","waitForHealthy","createBuckets","process","env","GCS_EMULATOR_URL","externalUrl","stop","container","containerName","remove","err","reset","bucket","buckets","fetch","name","method","getEmulatorHost","isHealthy","response","ok","existing","getContainer","info","inspect","catch","imageName","images","listImages","filters","JSON","stringify","reference","length","pullImage","Promise","resolve","reject","pull","stream","Error","modem","followProgress","_output","event","status","Image","HostConfig","PortBindings","HostPort","ExposedPorts","Cmd","maxAttempts","intervalMs","attempt","setTimeout","createRes","fetchWithTimeout","headers","body","statusText","message","String","url","init","timeoutMs","controller","AbortController","timer","abort","signal","aborted","clearTimeout","constructor","overrides"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -4,4 +4,7 @@
4
4
  export { GcsTwin } from './gcs-twin.js';
5
5
  export type { GcsTwinConfig, BucketConfig } from './gcs-twin.config.js';
6
6
  export { defaultGcsConfig } from './gcs-twin.config.js';
7
+ export { GcsErrorModeManager, extractBucketName, isUploadOperation, isGcsDataOperation, } from './gcs-error-mode.js';
8
+ export type { GcsErrorModeType, GcsErrorModeConfig, GcsErrorSimulationResult, BucketNotFoundConfig, QuotaExceededConfig, PermissionDeniedConfig, IntermittentFailureConfig, } from './gcs-error-mode.js';
9
+ export { GcsErrorSimulationInterceptor } from './gcs-error-simulation.interceptor.js';
7
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gcs/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gcs/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAC"}
package/dist/gcs/index.js CHANGED
@@ -2,5 +2,7 @@
2
2
  * Public API for the GCS twin.
3
3
  */ export { GcsTwin } from './gcs-twin.js';
4
4
  export { defaultGcsConfig } from './gcs-twin.config.js';
5
+ export { GcsErrorModeManager, extractBucketName, isUploadOperation, isGcsDataOperation } from './gcs-error-mode.js';
6
+ export { GcsErrorSimulationInterceptor } from './gcs-error-simulation.interceptor.js';
5
7
 
6
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/gcs/index.ts"],"sourcesContent":["/**\n * Public API for the GCS twin.\n */\n\nexport { GcsTwin } from './gcs-twin.js';\nexport type { GcsTwinConfig, BucketConfig } from './gcs-twin.config.js';\nexport { defaultGcsConfig } from './gcs-twin.config.js';\n"],"names":["GcsTwin","defaultGcsConfig"],"rangeMappings":";;;","mappings":"AAAA;;CAEC,GAED,SAASA,OAAO,QAAQ,gBAAgB;AAExC,SAASC,gBAAgB,QAAQ,uBAAuB"}
1
+ {"version":3,"sources":["../../src/gcs/index.ts"],"sourcesContent":["/**\n * Public API for the GCS twin.\n */\n\nexport { GcsTwin } from './gcs-twin.js';\nexport type { GcsTwinConfig, BucketConfig } from './gcs-twin.config.js';\nexport { defaultGcsConfig } from './gcs-twin.config.js';\n\nexport {\n GcsErrorModeManager,\n extractBucketName,\n isUploadOperation,\n isGcsDataOperation,\n} from './gcs-error-mode.js';\nexport type {\n GcsErrorModeType,\n GcsErrorModeConfig,\n GcsErrorSimulationResult,\n BucketNotFoundConfig,\n QuotaExceededConfig,\n PermissionDeniedConfig,\n IntermittentFailureConfig,\n} from './gcs-error-mode.js';\n\nexport { GcsErrorSimulationInterceptor } from './gcs-error-simulation.interceptor.js';\n"],"names":["GcsTwin","defaultGcsConfig","GcsErrorModeManager","extractBucketName","isUploadOperation","isGcsDataOperation","GcsErrorSimulationInterceptor"],"rangeMappings":";;;;;","mappings":"AAAA;;CAEC,GAED,SAASA,OAAO,QAAQ,gBAAgB;AAExC,SAASC,gBAAgB,QAAQ,uBAAuB;AAExD,SACEC,mBAAmB,EACnBC,iBAAiB,EACjBC,iBAAiB,EACjBC,kBAAkB,QACb,sBAAsB;AAW7B,SAASC,6BAA6B,QAAQ,wCAAwC"}
package/dist/index.d.ts CHANGED
@@ -3,4 +3,6 @@ export * from './shared/constants.js';
3
3
  export * from './shared/activation.js';
4
4
  export * from './shared/config.js';
5
5
  export * from './gcs/index.js';
6
+ export * from './youtube/index.js';
7
+ export * from './interceptor/index.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAGlD,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAGlD,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -5,5 +5,9 @@ export * from './shared/activation.js';
5
5
  export * from './shared/config.js';
6
6
  // GCS twin
7
7
  export * from './gcs/index.js';
8
+ // YouTube twin
9
+ export * from './youtube/index.js';
10
+ // Interceptor layer
11
+ export * from './interceptor/index.js';
8
12
 
9
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './lib/integration-test-doubles.js';\n\n// Shared constants and utilities\nexport * from './shared/constants.js';\nexport * from './shared/activation.js';\nexport * from './shared/config.js';\n\n// GCS twin\nexport * from './gcs/index.js';\n"],"names":[],"rangeMappings":";;;;;;","mappings":"AAAA,cAAc,oCAAoC;AAElD,iCAAiC;AACjC,cAAc,wBAAwB;AACtC,cAAc,yBAAyB;AACvC,cAAc,qBAAqB;AAEnC,WAAW;AACX,cAAc,iBAAiB"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './lib/integration-test-doubles.js';\n\n// Shared constants and utilities\nexport * from './shared/constants.js';\nexport * from './shared/activation.js';\nexport * from './shared/config.js';\n\n// GCS twin\nexport * from './gcs/index.js';\n\n// YouTube twin\nexport * from './youtube/index.js';\n\n// Interceptor layer\nexport * from './interceptor/index.js';\n"],"names":[],"rangeMappings":";;;;;;;;;;","mappings":"AAAA,cAAc,oCAAoC;AAElD,iCAAiC;AACjC,cAAc,wBAAwB;AACtC,cAAc,yBAAyB;AACvC,cAAc,qBAAqB;AAEnC,WAAW;AACX,cAAc,iBAAiB;AAE/B,eAAe;AACf,cAAc,qBAAqB;AAEnC,oBAAoB;AACpB,cAAc,yBAAyB"}
@@ -0,0 +1,37 @@
1
+ import http from 'http';
2
+ export interface ParsedRequestArgs {
3
+ urlObj: URL;
4
+ options: http.RequestOptions;
5
+ callback?: (res: http.IncomingMessage) => void;
6
+ }
7
+ /**
8
+ * Standardizes the polymorphic arguments of http.request / https.request
9
+ * into a single unified object with urlObj, options, and callback.
10
+ */
11
+ export declare function parseHttpArguments(moduleProtocol: string, arg1: string | URL | http.RequestOptions, arg2?: http.RequestOptions | ((res: http.IncomingMessage) => void), arg3?: (res: http.IncomingMessage) => void): ParsedRequestArgs;
12
+ /**
13
+ * Safely copies headers and injects the 'X-Original-Host' tracking header,
14
+ * handling both object-based (OutgoingHttpHeaders) and array-based (string[]) formats.
15
+ */
16
+ export declare function injectOriginalHostHeader(options: http.RequestOptions, originalHost: string): http.RequestOptions;
17
+ /**
18
+ * Creates new RequestOptions targeting a rewritten URL destination.
19
+ */
20
+ export declare function rewriteRequestOptions(options: http.RequestOptions, rewrittenUrl: URL): http.RequestOptions;
21
+ /**
22
+ * Safely invokes a request function with options and an optional callback.
23
+ */
24
+ export declare function callRequestWithOptions(requestFn: typeof http.request, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
25
+ /**
26
+ * Safely invokes a request function with a URL/string, options, and an optional callback.
27
+ */
28
+ export declare function callRequestWithUrlAndOptions(requestFn: typeof http.request, url: string | URL, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
29
+ /**
30
+ * Safely invokes a request function with a URL/string and an optional callback.
31
+ */
32
+ export declare function callRequestWithUrl(requestFn: typeof http.request, url: string | URL, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
33
+ /**
34
+ * Forwards arguments untouched to the original request function.
35
+ */
36
+ export declare function forwardToOriginalRequest(originalRequest: typeof http.request, arg1: string | URL | http.RequestOptions, arg2?: http.RequestOptions | ((res: http.IncomingMessage) => void), callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
37
+ //# sourceMappingURL=http-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-helper.d.ts","sourceRoot":"","sources":["../../src/interceptor/http-helper.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,cAAc,EACxC,IAAI,CAAC,EAAE,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,EAClE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,GACzC,iBAAiB,CAyCnB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,IAAI,CAAC,cAAc,EAC5B,YAAY,EAAE,MAAM,GACnB,IAAI,CAAC,cAAc,CAsBrB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CAAC,cAAc,EAC5B,YAAY,EAAE,GAAG,GAChB,IAAI,CAAC,cAAc,CAQrB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,OAAO,IAAI,CAAC,OAAO,EAC9B,OAAO,EAAE,IAAI,CAAC,cAAc,EAC5B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,GAC7C,IAAI,CAAC,aAAa,CAMpB;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,OAAO,IAAI,CAAC,OAAO,EAC9B,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,OAAO,EAAE,IAAI,CAAC,cAAc,EAC5B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,GAC7C,IAAI,CAAC,aAAa,CAOpB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,OAAO,IAAI,CAAC,OAAO,EAC9B,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,GAC7C,IAAI,CAAC,aAAa,CAMpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,OAAO,IAAI,CAAC,OAAO,EACpC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,cAAc,EACxC,IAAI,CAAC,EAAE,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,EAClE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,KAAK,IAAI,GAC7C,IAAI,CAAC,aAAa,CAiBpB"}
@@ -0,0 +1,109 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * Standardizes the polymorphic arguments of http.request / https.request
4
+ * into a single unified object with urlObj, options, and callback.
5
+ */ export function parseHttpArguments(moduleProtocol, arg1, arg2, arg3) {
6
+ let urlObj;
7
+ let options = {};
8
+ let callback = undefined;
9
+ if (typeof arg1 === 'string') {
10
+ urlObj = new URL(arg1);
11
+ if (typeof arg2 === 'object' && arg2 !== null) {
12
+ options = arg2;
13
+ callback = arg3;
14
+ } else if (typeof arg2 === 'function') {
15
+ callback = arg2;
16
+ }
17
+ } else if (arg1 instanceof URL) {
18
+ urlObj = arg1;
19
+ if (typeof arg2 === 'object' && arg2 !== null) {
20
+ options = arg2;
21
+ callback = arg3;
22
+ } else if (typeof arg2 === 'function') {
23
+ callback = arg2;
24
+ }
25
+ } else {
26
+ options = arg1 || {};
27
+ if (typeof arg2 === 'function') {
28
+ callback = arg2;
29
+ }
30
+ const protocol = options.protocol || moduleProtocol;
31
+ const host = options.host || (options.hostname ? `${options.hostname}${options.port !== undefined && options.port !== null ? `:${options.port}` : ''}` : '');
32
+ const path = options.path || '/';
33
+ urlObj = new URL(`${protocol}//${host}${path}`);
34
+ }
35
+ return {
36
+ urlObj,
37
+ options,
38
+ callback
39
+ };
40
+ }
41
+ /**
42
+ * Safely copies headers and injects the 'X-Original-Host' tracking header,
43
+ * handling both object-based (OutgoingHttpHeaders) and array-based (string[]) formats.
44
+ */ export function injectOriginalHostHeader(options, originalHost) {
45
+ const newOptions = _extends({}, options);
46
+ if (options.headers) {
47
+ if (Array.isArray(options.headers)) {
48
+ const headersArr = [
49
+ ...options.headers
50
+ ];
51
+ headersArr.push('X-Original-Host', originalHost);
52
+ newOptions.headers = headersArr;
53
+ } else {
54
+ const headersObj = _extends({}, options.headers);
55
+ headersObj['X-Original-Host'] = originalHost;
56
+ newOptions.headers = headersObj;
57
+ }
58
+ } else {
59
+ newOptions.headers = {
60
+ 'X-Original-Host': originalHost
61
+ };
62
+ }
63
+ return newOptions;
64
+ }
65
+ /**
66
+ * Creates new RequestOptions targeting a rewritten URL destination.
67
+ */ export function rewriteRequestOptions(options, rewrittenUrl) {
68
+ return _extends({}, options, {
69
+ protocol: rewrittenUrl.protocol,
70
+ hostname: rewrittenUrl.hostname,
71
+ port: rewrittenUrl.port,
72
+ path: rewrittenUrl.pathname + rewrittenUrl.search
73
+ });
74
+ }
75
+ /**
76
+ * Safely invokes a request function with options and an optional callback.
77
+ */ export function callRequestWithOptions(requestFn, options, callback) {
78
+ const fn = requestFn;
79
+ return fn(options, callback);
80
+ }
81
+ /**
82
+ * Safely invokes a request function with a URL/string, options, and an optional callback.
83
+ */ export function callRequestWithUrlAndOptions(requestFn, url, options, callback) {
84
+ const fn = requestFn;
85
+ return fn(url, options, callback);
86
+ }
87
+ /**
88
+ * Safely invokes a request function with a URL/string and an optional callback.
89
+ */ export function callRequestWithUrl(requestFn, url, callback) {
90
+ const fn = requestFn;
91
+ return fn(url, callback);
92
+ }
93
+ /**
94
+ * Forwards arguments untouched to the original request function.
95
+ */ export function forwardToOriginalRequest(originalRequest, arg1, arg2, callback) {
96
+ if (typeof arg1 === 'string' || arg1 instanceof URL) {
97
+ if (typeof arg2 === 'object' && arg2 !== null) {
98
+ return callRequestWithUrlAndOptions(originalRequest, arg1, arg2, callback);
99
+ } else {
100
+ const cb = typeof arg2 === 'function' ? arg2 : callback;
101
+ return callRequestWithUrl(originalRequest, arg1, cb);
102
+ }
103
+ } else {
104
+ const cb = typeof arg2 === 'function' ? arg2 : callback;
105
+ return callRequestWithOptions(originalRequest, arg1, cb);
106
+ }
107
+ }
108
+
109
+ //# sourceMappingURL=http-helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/interceptor/http-helper.ts"],"sourcesContent":["import http from 'http';\n\nexport interface ParsedRequestArgs {\n urlObj: URL;\n options: http.RequestOptions;\n callback?: (res: http.IncomingMessage) => void;\n}\n\n/**\n * Standardizes the polymorphic arguments of http.request / https.request\n * into a single unified object with urlObj, options, and callback.\n */\nexport function parseHttpArguments(\n moduleProtocol: string,\n arg1: string | URL | http.RequestOptions,\n arg2?: http.RequestOptions | ((res: http.IncomingMessage) => void),\n arg3?: (res: http.IncomingMessage) => void\n): ParsedRequestArgs {\n let urlObj: URL;\n let options: http.RequestOptions = {};\n let callback: ((res: http.IncomingMessage) => void) | undefined = undefined;\n\n if (typeof arg1 === 'string') {\n urlObj = new URL(arg1);\n if (typeof arg2 === 'object' && arg2 !== null) {\n options = arg2;\n callback = arg3;\n } else if (typeof arg2 === 'function') {\n callback = arg2;\n }\n } else if (arg1 instanceof URL) {\n urlObj = arg1;\n if (typeof arg2 === 'object' && arg2 !== null) {\n options = arg2;\n callback = arg3;\n } else if (typeof arg2 === 'function') {\n callback = arg2;\n }\n } else {\n options = arg1 || {};\n if (typeof arg2 === 'function') {\n callback = arg2;\n }\n const protocol = options.protocol || moduleProtocol;\n const host =\n options.host ||\n (options.hostname\n ? `${options.hostname}${\n options.port !== undefined && options.port !== null\n ? `:${options.port}`\n : ''\n }`\n : '');\n const path = options.path || '/';\n urlObj = new URL(`${protocol}//${host}${path}`);\n }\n\n return { urlObj, options, callback };\n}\n\n/**\n * Safely copies headers and injects the 'X-Original-Host' tracking header,\n * handling both object-based (OutgoingHttpHeaders) and array-based (string[]) formats.\n */\nexport function injectOriginalHostHeader(\n options: http.RequestOptions,\n originalHost: string\n): http.RequestOptions {\n const newOptions = { ...options };\n\n if (options.headers) {\n if (Array.isArray(options.headers)) {\n const headersArr = [...options.headers];\n headersArr.push('X-Original-Host', originalHost);\n newOptions.headers = headersArr;\n } else {\n const headersObj: http.OutgoingHttpHeaders = {\n ...(options.headers as http.OutgoingHttpHeaders),\n };\n headersObj['X-Original-Host'] = originalHost;\n newOptions.headers = headersObj;\n }\n } else {\n newOptions.headers = {\n 'X-Original-Host': originalHost,\n };\n }\n\n return newOptions;\n}\n\n/**\n * Creates new RequestOptions targeting a rewritten URL destination.\n */\nexport function rewriteRequestOptions(\n options: http.RequestOptions,\n rewrittenUrl: URL\n): http.RequestOptions {\n return {\n ...options,\n protocol: rewrittenUrl.protocol,\n hostname: rewrittenUrl.hostname,\n port: rewrittenUrl.port,\n path: rewrittenUrl.pathname + rewrittenUrl.search,\n };\n}\n\n/**\n * Safely invokes a request function with options and an optional callback.\n */\nexport function callRequestWithOptions(\n requestFn: typeof http.request,\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n): http.ClientRequest {\n const fn = requestFn as (\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n ) => http.ClientRequest;\n return fn(options, callback);\n}\n\n/**\n * Safely invokes a request function with a URL/string, options, and an optional callback.\n */\nexport function callRequestWithUrlAndOptions(\n requestFn: typeof http.request,\n url: string | URL,\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n): http.ClientRequest {\n const fn = requestFn as (\n url: string | URL,\n options: http.RequestOptions,\n callback?: (res: http.IncomingMessage) => void\n ) => http.ClientRequest;\n return fn(url, options, callback);\n}\n\n/**\n * Safely invokes a request function with a URL/string and an optional callback.\n */\nexport function callRequestWithUrl(\n requestFn: typeof http.request,\n url: string | URL,\n callback?: (res: http.IncomingMessage) => void\n): http.ClientRequest {\n const fn = requestFn as (\n url: string | URL,\n callback?: (res: http.IncomingMessage) => void\n ) => http.ClientRequest;\n return fn(url, callback);\n}\n\n/**\n * Forwards arguments untouched to the original request function.\n */\nexport function forwardToOriginalRequest(\n originalRequest: typeof http.request,\n arg1: string | URL | http.RequestOptions,\n arg2?: http.RequestOptions | ((res: http.IncomingMessage) => void),\n callback?: (res: http.IncomingMessage) => void\n): http.ClientRequest {\n if (typeof arg1 === 'string' || arg1 instanceof URL) {\n if (typeof arg2 === 'object' && arg2 !== null) {\n return callRequestWithUrlAndOptions(\n originalRequest,\n arg1,\n arg2,\n callback\n );\n } else {\n const cb = typeof arg2 === 'function' ? arg2 : callback;\n return callRequestWithUrl(originalRequest, arg1, cb);\n }\n } else {\n const cb = typeof arg2 === 'function' ? arg2 : callback;\n return callRequestWithOptions(originalRequest, arg1, cb);\n }\n}\n"],"names":["parseHttpArguments","moduleProtocol","arg1","arg2","arg3","urlObj","options","callback","undefined","URL","protocol","host","hostname","port","path","injectOriginalHostHeader","originalHost","newOptions","headers","Array","isArray","headersArr","push","headersObj","rewriteRequestOptions","rewrittenUrl","pathname","search","callRequestWithOptions","requestFn","fn","callRequestWithUrlAndOptions","url","callRequestWithUrl","forwardToOriginalRequest","originalRequest","cb"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './routing-table.js';
2
+ export * from './routing-table.config.js';
3
+ export * from './node-interceptor.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/interceptor/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from './routing-table.js';
2
+ export * from './routing-table.config.js';
3
+ export * from './node-interceptor.js';
4
+
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/interceptor/index.ts"],"sourcesContent":["export * from './routing-table.js';\nexport * from './routing-table.config.js';\nexport * from './node-interceptor.js';\n"],"names":[],"rangeMappings":";;","mappings":"AAAA,cAAc,qBAAqB;AACnC,cAAc,4BAA4B;AAC1C,cAAc,wBAAwB"}
@@ -0,0 +1,40 @@
1
+ import { RoutingTable } from './routing-table.js';
2
+ export declare class NodeInterceptor {
3
+ private originalFetch;
4
+ private originalHttpRequest;
5
+ private originalHttpGet;
6
+ private originalHttpsRequest;
7
+ private originalHttpsGet;
8
+ private readonly routingTable;
9
+ private active;
10
+ constructor(routingTable: RoutingTable);
11
+ /**
12
+ * Installs interception hooks for both global fetch and node http/https modules.
13
+ */
14
+ install(): void;
15
+ /**
16
+ * Restores fetch and http/https modules to their original native states.
17
+ */
18
+ uninstall(): void;
19
+ /**
20
+ * Checks if interception is currently active.
21
+ */
22
+ isActive(): boolean;
23
+ /**
24
+ * Monkey-patches the global fetch API to redirect requests based on the RoutingTable.
25
+ */
26
+ private interceptFetch;
27
+ /**
28
+ * Monkey-patches Node's native http/https request methods.
29
+ */
30
+ private interceptHttpAndHttpsRequests;
31
+ /**
32
+ * Restores global fetch to its original implementation.
33
+ */
34
+ private restoreFetch;
35
+ /**
36
+ * Restores Node's http/https methods to their original implementations.
37
+ */
38
+ private restoreHttpAndHttpsRequests;
39
+ }
40
+ //# sourceMappingURL=node-interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-interceptor.d.ts","sourceRoot":"","sources":["../../src/interceptor/node-interceptor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAWlD,qBAAa,eAAe;IAC1B,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,MAAM,CAAS;gBAEX,YAAY,EAAE,YAAY;IAItC;;OAEG;IACH,OAAO,IAAI,IAAI;IAWf;;OAEG;IACH,SAAS,IAAI,IAAI;IAWjB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,OAAO,CAAC,cAAc;IAiFtB;;OAEG;IACH,OAAO,CAAC,6BAA6B;IA+FrC;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,2BAA2B;CAqBpC"}
@@ -0,0 +1,188 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import http from 'http';
3
+ import https from 'https';
4
+ import { parseHttpArguments, injectOriginalHostHeader, rewriteRequestOptions, callRequestWithOptions, callRequestWithUrlAndOptions, callRequestWithUrl, forwardToOriginalRequest } from './http-helper.js';
5
+ export class NodeInterceptor {
6
+ /**
7
+ * Installs interception hooks for both global fetch and node http/https modules.
8
+ */ install() {
9
+ if (this.active) {
10
+ return; // Idempotency guard
11
+ }
12
+ this.interceptFetch();
13
+ this.interceptHttpAndHttpsRequests();
14
+ this.active = true;
15
+ }
16
+ /**
17
+ * Restores fetch and http/https modules to their original native states.
18
+ */ uninstall() {
19
+ if (!this.active) {
20
+ return;
21
+ }
22
+ this.restoreFetch();
23
+ this.restoreHttpAndHttpsRequests();
24
+ this.active = false;
25
+ }
26
+ /**
27
+ * Checks if interception is currently active.
28
+ */ isActive() {
29
+ return this.active;
30
+ }
31
+ /**
32
+ * Monkey-patches the global fetch API to redirect requests based on the RoutingTable.
33
+ */ interceptFetch() {
34
+ this.originalFetch = globalThis.fetch;
35
+ const originalFetch = this.originalFetch;
36
+ const routingTable = this.routingTable;
37
+ globalThis.fetch = async (input, init)=>{
38
+ if (!originalFetch) {
39
+ throw new Error('NodeInterceptor: original fetch is not cached');
40
+ }
41
+ let urlStr = '';
42
+ if (typeof input === 'string') {
43
+ urlStr = input;
44
+ } else if (input instanceof URL) {
45
+ urlStr = input.toString();
46
+ } else if (input instanceof Request) {
47
+ urlStr = input.url;
48
+ }
49
+ const rewritten = routingTable.resolve(urlStr);
50
+ if (rewritten) {
51
+ const originalHost = new URL(urlStr).host;
52
+ const headers = new Headers();
53
+ // 1. Copy headers from input Request if it exists
54
+ if (input instanceof Request) {
55
+ input.headers.forEach((value, key)=>{
56
+ headers.set(key, value);
57
+ });
58
+ }
59
+ // 2. Overwrite with any headers passed in init
60
+ if (init == null ? void 0 : init.headers) {
61
+ const initHeaders = new Headers(init.headers);
62
+ initHeaders.forEach((value, key)=>{
63
+ headers.set(key, value);
64
+ });
65
+ }
66
+ // 3. Inject X-Original-Host tracking header
67
+ headers.set('X-Original-Host', originalHost);
68
+ // Handle native Request object conversion cleanly
69
+ if (input instanceof Request) {
70
+ const newInit = _extends({
71
+ method: input.method,
72
+ headers: headers,
73
+ body: input.body,
74
+ signal: input.signal
75
+ }, init);
76
+ if (input.body) {
77
+ newInit.duplex = 'half';
78
+ }
79
+ if ('credentials' in input) newInit.credentials = input.credentials;
80
+ if ('mode' in input) newInit.mode = input.mode;
81
+ if ('referrer' in input) newInit.referrer = input.referrer;
82
+ if ('cache' in input) newInit.cache = input.cache;
83
+ if ('redirect' in input) newInit.redirect = input.redirect;
84
+ const newRequest = new Request(rewritten, newInit);
85
+ return originalFetch(newRequest);
86
+ }
87
+ // Handle string/URL input
88
+ return originalFetch(rewritten, _extends({}, init, {
89
+ headers
90
+ }));
91
+ }
92
+ // No match - forward request untouched
93
+ return originalFetch(input, init);
94
+ };
95
+ }
96
+ /**
97
+ * Monkey-patches Node's native http/https request methods.
98
+ */ interceptHttpAndHttpsRequests() {
99
+ this.originalHttpRequest = http.request;
100
+ this.originalHttpGet = http.get;
101
+ this.originalHttpsRequest = https.request;
102
+ this.originalHttpsGet = https.get;
103
+ // Helper to generate a patched request method for http or https
104
+ const createPatchedRequest = (moduleProtocol, originalRequest)=>{
105
+ return (arg1, arg2, arg3)=>{
106
+ // Parse polymorphic arguments into standardized request variables
107
+ const { urlObj, options, callback } = parseHttpArguments(moduleProtocol, arg1, arg2, arg3);
108
+ const rewrittenStr = this.routingTable.resolve(urlObj.toString());
109
+ if (rewrittenStr) {
110
+ const rewritten = new URL(rewrittenStr);
111
+ // 1. Route to local digital twin emulator target
112
+ let newOptions = rewriteRequestOptions(options, rewritten);
113
+ // 2. Add verification header to store original destination
114
+ newOptions = injectOriginalHostHeader(newOptions, urlObj.host);
115
+ const targetRequest = rewritten.protocol === 'https:' ? this.originalHttpsRequest : this.originalHttpRequest;
116
+ if (!targetRequest) {
117
+ throw new Error('NodeInterceptor: Cannot call targetRequest because the interceptor has not been fully installed.');
118
+ }
119
+ return callRequestWithOptions(targetRequest, newOptions, callback);
120
+ }
121
+ // Pass-through to original network endpoint if no matching redirection rule is found
122
+ return forwardToOriginalRequest(originalRequest, arg1, arg2, callback);
123
+ };
124
+ };
125
+ // Helper to generate a patched get method (wraps the request method and auto-ends the request stream)
126
+ const createPatchedGet = (patchedRequest)=>{
127
+ return (arg1, arg2, arg3)=>{
128
+ let req;
129
+ if (typeof arg1 === 'string' || arg1 instanceof URL) {
130
+ if (typeof arg2 === 'object' && arg2 !== null) {
131
+ req = callRequestWithUrlAndOptions(patchedRequest, arg1, arg2, arg3);
132
+ } else {
133
+ const callback = typeof arg2 === 'function' ? arg2 : arg3;
134
+ req = callRequestWithUrl(patchedRequest, arg1, callback);
135
+ }
136
+ } else {
137
+ const callback = typeof arg2 === 'function' ? arg2 : arg3;
138
+ req = callRequestWithOptions(patchedRequest, arg1, callback);
139
+ }
140
+ req.end();
141
+ return req;
142
+ };
143
+ };
144
+ http.request = createPatchedRequest('http:', this.originalHttpRequest);
145
+ https.request = createPatchedRequest('https:', this.originalHttpsRequest);
146
+ http.get = createPatchedGet(http.request);
147
+ https.get = createPatchedGet(https.request);
148
+ }
149
+ /**
150
+ * Restores global fetch to its original implementation.
151
+ */ restoreFetch() {
152
+ if (this.originalFetch) {
153
+ globalThis.fetch = this.originalFetch;
154
+ this.originalFetch = null;
155
+ }
156
+ }
157
+ /**
158
+ * Restores Node's http/https methods to their original implementations.
159
+ */ restoreHttpAndHttpsRequests() {
160
+ if (this.originalHttpRequest) {
161
+ http.request = this.originalHttpRequest;
162
+ this.originalHttpRequest = null;
163
+ }
164
+ if (this.originalHttpGet) {
165
+ http.get = this.originalHttpGet;
166
+ this.originalHttpGet = null;
167
+ }
168
+ if (this.originalHttpsRequest) {
169
+ https.request = this.originalHttpsRequest;
170
+ this.originalHttpsRequest = null;
171
+ }
172
+ if (this.originalHttpsGet) {
173
+ https.get = this.originalHttpsGet;
174
+ this.originalHttpsGet = null;
175
+ }
176
+ }
177
+ constructor(routingTable){
178
+ this.originalFetch = null;
179
+ this.originalHttpRequest = null;
180
+ this.originalHttpGet = null;
181
+ this.originalHttpsRequest = null;
182
+ this.originalHttpsGet = null;
183
+ this.active = false;
184
+ this.routingTable = routingTable;
185
+ }
186
+ }
187
+
188
+ //# sourceMappingURL=node-interceptor.js.map