@things-factory/integration-base 9.0.33 → 9.0.35

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 (34) hide show
  1. package/dist-server/engine/connector/headless-connector.d.ts +1 -0
  2. package/dist-server/engine/connector/headless-connector.js +117 -61
  3. package/dist-server/engine/connector/headless-connector.js.map +1 -1
  4. package/dist-server/engine/connector/mssql-connector.js +91 -11
  5. package/dist-server/engine/connector/mssql-connector.js.map +1 -1
  6. package/dist-server/engine/resource-pool/headless-pool.d.ts +29 -1
  7. package/dist-server/engine/resource-pool/headless-pool.js +70 -53
  8. package/dist-server/engine/resource-pool/headless-pool.js.map +1 -1
  9. package/dist-server/engine/task/headless-delete.js +9 -61
  10. package/dist-server/engine/task/headless-delete.js.map +1 -1
  11. package/dist-server/engine/task/headless-get.js +9 -55
  12. package/dist-server/engine/task/headless-get.js.map +1 -1
  13. package/dist-server/engine/task/headless-patch.js +11 -74
  14. package/dist-server/engine/task/headless-patch.js.map +1 -1
  15. package/dist-server/engine/task/headless-post.js +11 -74
  16. package/dist-server/engine/task/headless-post.js.map +1 -1
  17. package/dist-server/engine/task/headless-put.js +11 -74
  18. package/dist-server/engine/task/headless-put.js.map +1 -1
  19. package/dist-server/engine/task/mssql-procedure.js +1 -1
  20. package/dist-server/engine/task/mssql-procedure.js.map +1 -1
  21. package/dist-server/engine/task/utils/headless-request-with-recovery.d.ts +18 -0
  22. package/dist-server/engine/task/utils/headless-request-with-recovery.js +221 -0
  23. package/dist-server/engine/task/utils/headless-request-with-recovery.js.map +1 -0
  24. package/dist-server/restful/unstable/headless-pool-status.d.ts +1 -0
  25. package/dist-server/restful/unstable/headless-pool-status.js +78 -0
  26. package/dist-server/restful/unstable/headless-pool-status.js.map +1 -0
  27. package/dist-server/restful/unstable/index.d.ts +1 -0
  28. package/dist-server/restful/unstable/index.js +1 -0
  29. package/dist-server/restful/unstable/index.js.map +1 -1
  30. package/dist-server/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +8 -9
  32. package/dist-server/engine/task/utils/headless-pool-for-scenario.d.ts +0 -1
  33. package/dist-server/engine/task/utils/headless-pool-for-scenario.js +0 -64
  34. package/dist-server/engine/task/utils/headless-pool-for-scenario.js.map +0 -1
@@ -1,62 +1,79 @@
1
1
  "use strict";
2
+ /**
3
+ * Integration Base Headless Pool
4
+ * Using the unified headless pool system from @things-factory/shell
5
+ */
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
3
7
  exports.getHeadlessPool = getHeadlessPool;
4
- const tslib_1 = require("tslib");
5
- const genericPool = tslib_1.__importStar(require("generic-pool"));
6
- const env_1 = require("@things-factory/env");
7
- try {
8
- var puppeteer = require('puppeteer');
9
- }
10
- catch (err) {
11
- env_1.logger.error(err);
12
- }
13
- let headlessPool;
8
+ exports.default = getHeadlessPool;
9
+ exports.getPoolStats = getPoolStats;
10
+ exports.forceCleanupPool = forceCleanupPool;
11
+ const shell_1 = require("@things-factory/shell");
12
+ // Create or get the integration pool instance
13
+ const integrationPool = (0, shell_1.getOrCreateHeadlessPool)('integration', {
14
+ min: 2,
15
+ max: 20,
16
+ args: [...shell_1.HEADLESS_POOL_ARGUMENT_SETS.basic, ...shell_1.HEADLESS_POOL_ARGUMENT_SETS.keychain_safe],
17
+ acquireTimeoutMillis: 15000,
18
+ testOnBorrow: true,
19
+ enableStats: true,
20
+ enableRecovery: true,
21
+ enableCleanup: true
22
+ });
23
+ /**
24
+ * Get the integration headless pool
25
+ * @returns Pool instance with acquire/release methods
26
+ */
14
27
  function getHeadlessPool() {
15
- if (!headlessPool) {
16
- headlessPool = createHeadlessPool({ min: 2, max: 20, acquireTimeoutMillis: 15000, testOnBorrow: true });
17
- }
18
- return headlessPool;
28
+ return {
29
+ acquire: () => integrationPool.acquire(),
30
+ release: (resource) => integrationPool.release(resource),
31
+ size: 0, // These will be dynamically calculated if needed
32
+ available: 0,
33
+ borrowed: 0,
34
+ pending: 0,
35
+ max: 20,
36
+ min: 2
37
+ };
19
38
  }
20
- function createHeadlessPool(options) {
21
- return genericPool.createPool({
22
- create() {
23
- console.log('headless instance in headless-pool-integration about to create');
24
- return initializeChromium();
25
- },
26
- validate(browser) {
27
- return Promise.race([
28
- new Promise(res => setTimeout(() => res(false), 1500)),
29
- browser
30
- //@ts-ignore
31
- .version()
32
- .then(() => true)
33
- .catch(() => false)
34
- ]);
35
- },
36
- destroy(browser) {
37
- //@ts-ignore
38
- return browser.close();
39
- }
40
- }, options);
41
- }
42
- const CHROMIUM_PATH = env_1.config.get('CHROMIUM_PATH');
43
- async function initializeChromium() {
44
- try {
45
- if (!puppeteer) {
46
- return;
47
- }
48
- const launchSetting = {
49
- args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--use-gl=egl'],
50
- headless: 'shell'
39
+ /**
40
+ * Get pool statistics
41
+ * @returns Detailed pool statistics
42
+ */
43
+ function getPoolStats() {
44
+ const allStats = (0, shell_1.getAllPoolStats)();
45
+ const integrationStats = allStats.integration;
46
+ if (!integrationStats) {
47
+ return {
48
+ size: 0,
49
+ available: 0,
50
+ borrowed: 0,
51
+ pending: 0,
52
+ max: 20,
53
+ min: 2,
54
+ totalCreated: 0,
55
+ totalDestroyed: 0,
56
+ totalAcquired: 0,
57
+ totalReleased: 0,
58
+ currentlyAcquired: 0,
59
+ averageAcquisitionTime: 0,
60
+ utilizationRate: '0',
61
+ lastActivity: null
51
62
  };
52
- if (CHROMIUM_PATH) {
53
- launchSetting['executablePath'] = CHROMIUM_PATH;
54
- }
55
- const browser = await puppeteer.launch(launchSetting);
56
- return browser;
57
- }
58
- catch (err) {
59
- env_1.logger.error(err);
60
63
  }
64
+ // Convert to format expected by existing code
65
+ return {
66
+ ...integrationStats.poolStatus,
67
+ ...integrationStats.statistics,
68
+ averageAcquisitionTime: parseInt(integrationStats.statistics.averageAcquisitionTime.replace('ms', '')) || 0,
69
+ utilizationRate: integrationStats.poolStatus.utilizationRate.replace('%', '')
70
+ };
71
+ }
72
+ /**
73
+ * Force cleanup pool - delegates to unified system
74
+ * @returns Promise<void>
75
+ */
76
+ function forceCleanupPool() {
77
+ return integrationPool.reset();
61
78
  }
62
79
  //# sourceMappingURL=headless-pool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"headless-pool.js","sourceRoot":"","sources":["../../../server/engine/resource-pool/headless-pool.ts"],"names":[],"mappings":";;AAWA,0CAMC;;AAjBD,kEAA2C;AAC3C,6CAAoD;AAEpD,IAAI,CAAC;IACH,IAAI,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;AACtC,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACb,YAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AACnB,CAAC;AAED,IAAI,YAAY,CAAA;AAEhB,SAAgB,eAAe;IAC7B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAA;IACzG,CAAC;IAED,OAAO,YAAY,CAAA;AACrB,CAAC;AAED,SAAS,kBAAkB,CAAC,OAA4B;IACtD,OAAO,WAAW,CAAC,UAAU,CAC3B;QACE,MAAM;YACJ,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAA;YAC7E,OAAO,kBAAkB,EAAE,CAAA;QAC7B,CAAC;QACD,QAAQ,CAAC,OAAO;YACd,OAAO,OAAO,CAAC,IAAI,CAAC;gBAClB,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;gBACtD,OAAO;oBACL,YAAY;qBACX,OAAO,EAAE;qBACT,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;qBAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;aACtB,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,CAAC,OAAO;YACb,YAAY;YACZ,OAAO,OAAO,CAAC,KAAK,EAAE,CAAA;QACxB,CAAC;KAC0B,EAC7B,OAAO,CACR,CAAA;AACH,CAAC;AAED,MAAM,aAAa,GAAG,YAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;AAEjD,KAAK,UAAU,kBAAkB;IAC/B,IAAI,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,MAAM,aAAa,GAAG;YACpB,IAAI,EAAE,CAAC,mBAAmB,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,CAAC;YAC3E,QAAQ,EAAE,OAAO;SAClB,CAAA;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,aAAa,CAAC,gBAAgB,CAAC,GAAG,aAAa,CAAA;QACjD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QAErD,OAAO,OAAO,CAAA;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACnB,CAAC;AACH,CAAC","sourcesContent":["import * as genericPool from 'generic-pool'\nimport { config, logger } from '@things-factory/env'\n\ntry {\n var puppeteer = require('puppeteer')\n} catch (err) {\n logger.error(err)\n}\n\nlet headlessPool\n\nexport function getHeadlessPool() {\n if (!headlessPool) {\n headlessPool = createHeadlessPool({ min: 2, max: 20, acquireTimeoutMillis: 15000, testOnBorrow: true })\n }\n\n return headlessPool\n}\n\nfunction createHeadlessPool(options: genericPool.Options) {\n return genericPool.createPool(\n {\n create() {\n console.log('headless instance in headless-pool-integration about to create')\n return initializeChromium()\n },\n validate(browser) {\n return Promise.race([\n new Promise(res => setTimeout(() => res(false), 1500)),\n browser\n //@ts-ignore\n .version()\n .then(() => true)\n .catch(() => false)\n ])\n },\n destroy(browser) {\n //@ts-ignore\n return browser.close()\n }\n } as genericPool.Factory<any>,\n options\n )\n}\n\nconst CHROMIUM_PATH = config.get('CHROMIUM_PATH')\n\nasync function initializeChromium() {\n try {\n if (!puppeteer) {\n return\n }\n\n const launchSetting = {\n args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--use-gl=egl'],\n headless: 'shell'\n }\n\n if (CHROMIUM_PATH) {\n launchSetting['executablePath'] = CHROMIUM_PATH\n }\n\n const browser = await puppeteer.launch(launchSetting)\n\n return browser\n } catch (err) {\n logger.error(err)\n }\n}\n"]}
1
+ {"version":3,"file":"headless-pool.js","sourceRoot":"","sources":["../../../server/engine/resource-pool/headless-pool.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAoBH,0CAWC;AA+C2B,kCAAO;AAzCnC,oCA8BC;AAMD,4CAEC;AAzED,iDAA6G;AAE7G,8CAA8C;AAC9C,MAAM,eAAe,GAAG,IAAA,+BAAuB,EAAC,aAAa,EAAE;IAC7D,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,EAAE;IACP,IAAI,EAAE,CAAC,GAAG,mCAA2B,CAAC,KAAK,EAAE,GAAG,mCAA2B,CAAC,aAAa,CAAC;IAC1F,oBAAoB,EAAE,KAAK;IAC3B,YAAY,EAAE,IAAI;IAClB,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,IAAI;CACpB,CAAC,CAAA;AAEF;;;GAGG;AACH,SAAgB,eAAe;IAC7B,OAAO;QACL,OAAO,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,OAAO,EAAE;QACxC,OAAO,EAAE,CAAC,QAAa,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC7D,IAAI,EAAE,CAAC,EAAE,iDAAiD;QAC1D,SAAS,EAAE,CAAC;QACZ,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,CAAC;QACV,GAAG,EAAE,EAAE;QACP,GAAG,EAAE,CAAC;KACP,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY;IAC1B,MAAM,QAAQ,GAAG,IAAA,uBAAe,GAAE,CAAA;IAClC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,WAAW,CAAA;IAE7C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO;YACL,IAAI,EAAE,CAAC;YACP,SAAS,EAAE,CAAC;YACZ,QAAQ,EAAE,CAAC;YACX,OAAO,EAAE,CAAC;YACV,GAAG,EAAE,EAAE;YACP,GAAG,EAAE,CAAC;YACN,YAAY,EAAE,CAAC;YACf,cAAc,EAAE,CAAC;YACjB,aAAa,EAAE,CAAC;YAChB,aAAa,EAAE,CAAC;YAChB,iBAAiB,EAAE,CAAC;YACpB,sBAAsB,EAAE,CAAC;YACzB,eAAe,EAAE,GAAG;YACpB,YAAY,EAAE,IAAI;SACnB,CAAA;IACH,CAAC;IAED,8CAA8C;IAC9C,OAAO;QACL,GAAG,gBAAgB,CAAC,UAAU;QAC9B,GAAG,gBAAgB,CAAC,UAAU;QAC9B,sBAAsB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,UAAU,CAAC,sBAAsB,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC;QAC3G,eAAe,EAAE,gBAAgB,CAAC,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;KAC9E,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB;IAC9B,OAAO,eAAe,CAAC,KAAK,EAAE,CAAA;AAChC,CAAC","sourcesContent":["/**\n * Integration Base Headless Pool\n * Using the unified headless pool system from @things-factory/shell\n */\n\nimport { getOrCreateHeadlessPool, getAllPoolStats, HEADLESS_POOL_ARGUMENT_SETS } from '@things-factory/shell'\n\n// Create or get the integration pool instance\nconst integrationPool = getOrCreateHeadlessPool('integration', {\n min: 2,\n max: 20,\n args: [...HEADLESS_POOL_ARGUMENT_SETS.basic, ...HEADLESS_POOL_ARGUMENT_SETS.keychain_safe],\n acquireTimeoutMillis: 15000,\n testOnBorrow: true,\n enableStats: true,\n enableRecovery: true,\n enableCleanup: true\n})\n\n/**\n * Get the integration headless pool\n * @returns Pool instance with acquire/release methods\n */\nexport function getHeadlessPool() {\n return {\n acquire: () => integrationPool.acquire(),\n release: (resource: any) => integrationPool.release(resource),\n size: 0, // These will be dynamically calculated if needed\n available: 0,\n borrowed: 0,\n pending: 0,\n max: 20,\n min: 2\n }\n}\n\n/**\n * Get pool statistics\n * @returns Detailed pool statistics\n */\nexport function getPoolStats() {\n const allStats = getAllPoolStats()\n const integrationStats = allStats.integration\n\n if (!integrationStats) {\n return {\n size: 0,\n available: 0,\n borrowed: 0,\n pending: 0,\n max: 20,\n min: 2,\n totalCreated: 0,\n totalDestroyed: 0,\n totalAcquired: 0,\n totalReleased: 0,\n currentlyAcquired: 0,\n averageAcquisitionTime: 0,\n utilizationRate: '0',\n lastActivity: null\n }\n }\n\n // Convert to format expected by existing code\n return {\n ...integrationStats.poolStatus,\n ...integrationStats.statistics,\n averageAcquisitionTime: parseInt(integrationStats.statistics.averageAcquisitionTime.replace('ms', '')) || 0,\n utilizationRate: integrationStats.poolStatus.utilizationRate.replace('%', '')\n }\n}\n\n/**\n * Force cleanup pool - delegates to unified system\n * @returns Promise<void>\n */\nexport function forceCleanupPool(): Promise<void> {\n return integrationPool.reset()\n}\n\n// Legacy compatibility - re-export the main function\nexport { getHeadlessPool as default }\n"]}
@@ -1,78 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const https_1 = tslib_1.__importDefault(require("https"));
5
- const url_1 = require("url");
6
3
  const task_registry_1 = require("../task-registry");
7
- const connection_manager_1 = require("../connection-manager");
4
+ const headless_request_with_recovery_1 = require("./utils/headless-request-with-recovery");
8
5
  async function HeadlessDelete(step, { logger, data, domain }) {
9
6
  const { connection: connectionName, params: stepOptions } = step;
10
7
  const { headers: requestHeaders, path, queryParams } = stepOptions || {};
11
- const connection = await connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
12
- if (!connection) {
13
- throw new Error(`Connection '${connectionName}' is not established.`);
14
- }
15
- const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection;
16
- const headers = {
17
- ...requestHeaders
18
- };
19
- const options = {
20
- method: 'DELETE',
21
- headers
22
- };
23
- const { rejectUnauthorized } = connectionParams;
24
- if (!rejectUnauthorized) {
25
- const httpsAgent = new https_1.default.Agent({
26
- rejectUnauthorized
27
- });
28
- options.agent = httpsAgent;
29
- }
30
- const page = await acquireSessionPage();
31
8
  try {
32
- page.on('console', async (msg) => {
33
- console.log(`[browser ${msg.type()}] ${msg.text()}`);
34
- });
35
- page.on('requestfailed', request => {
36
- console.log('Request failed:', request.url());
37
- });
38
- await page.goto(endpoint, { waitUntil: 'networkidle2' });
39
- // URL 구성 - queryParams가 있으면 추가
40
- let requestUrl = new url_1.URL(path, endpoint);
41
- if (queryParams && typeof queryParams === 'object') {
42
- Object.keys(queryParams).forEach(key => {
43
- if (queryParams[key] !== null && queryParams[key] !== undefined) {
44
- requestUrl.searchParams.append(key, String(queryParams[key]));
45
- }
46
- });
47
- }
48
- const response = await page.evaluate(async (urlString, options) => {
49
- const response = await fetch(urlString, options);
50
- if (!response.ok) {
51
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
52
- }
53
- const contentType = response.headers.get('content-type') || '';
54
- if (contentType.includes('application/json')) {
55
- return await response.json();
56
- }
57
- else if (contentType.includes('text/')) {
58
- return await response.text();
59
- }
60
- else {
61
- // 응답이 없는 경우 (204 No Content 등)
62
- return null;
63
- }
64
- }, requestUrl.toString(), options);
9
+ const result = await (0, headless_request_with_recovery_1.executeHeadlessRequestWithRecovery)(connectionName, {
10
+ method: 'DELETE',
11
+ path,
12
+ headers: requestHeaders,
13
+ queryParams,
14
+ maxRetries: 2
15
+ }, { logger, data, domain });
65
16
  return {
66
- data: response
17
+ data: result.data
67
18
  };
68
19
  }
69
20
  catch (error) {
70
21
  logger.error('Error in HeadlessDelete:', error);
71
22
  throw error;
72
23
  }
73
- finally {
74
- await releasePage(page);
75
- }
76
24
  }
77
25
  HeadlessDelete.parameterSpec = [
78
26
  {
@@ -1 +1 @@
1
- {"version":3,"file":"headless-delete.js","sourceRoot":"","sources":["../../../server/engine/task/headless-delete.ts"],"names":[],"mappings":"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","sourcesContent":["import https from 'https'\nimport { URL } from 'url'\n\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nasync function HeadlessDelete(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, path, queryParams } = stepOptions || {}\n\n const connection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection\n\n const headers = {\n ...requestHeaders\n }\n\n const options = {\n method: 'DELETE',\n headers\n } as any\n\n const { rejectUnauthorized } = connectionParams\n\n if (!rejectUnauthorized) {\n const httpsAgent = new https.Agent({\n rejectUnauthorized\n })\n options.agent = httpsAgent\n }\n\n const page = await acquireSessionPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n await page.goto(endpoint, { waitUntil: 'networkidle2' })\n\n // URL 구성 - queryParams가 있으면 추가\n let requestUrl = new URL(path, endpoint)\n if (queryParams && typeof queryParams === 'object') {\n Object.keys(queryParams).forEach(key => {\n if (queryParams[key] !== null && queryParams[key] !== undefined) {\n requestUrl.searchParams.append(key, String(queryParams[key]))\n }\n })\n }\n\n const response = await page.evaluate(\n async (urlString, options) => {\n const response = await fetch(urlString, options)\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const contentType = response.headers.get('content-type') || ''\n \n if (contentType.includes('application/json')) {\n return await response.json()\n } else if (contentType.includes('text/')) {\n return await response.text()\n } else {\n // 응답이 없는 경우 (204 No Content 등)\n return null\n }\n },\n requestUrl.toString(),\n options\n )\n\n return {\n data: response\n }\n } catch (error) {\n logger.error('Error in HeadlessDelete:', error)\n throw error\n } finally {\n await releasePage(page)\n }\n}\n\nHeadlessDelete.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'queryParams',\n label: 'query-parameters'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-delete', HeadlessDelete)"]}
1
+ {"version":3,"file":"headless-delete.js","sourceRoot":"","sources":["../../../server/engine/task/headless-delete.ts"],"names":[],"mappings":";;AAAA,oDAA+C;AAC/C,2FAA2F;AAE3F,KAAK,UAAU,cAAc,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IAC1D,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IAChE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,IAAI,EAAE,CAAA;IAExE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,mEAAkC,EACrD,cAAc,EACd;YACE,MAAM,EAAE,QAAQ;YAChB,IAAI;YACJ,OAAO,EAAE,cAAc;YACvB,WAAW;YACX,UAAU,EAAE,CAAC;SACd,EACD,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CACzB,CAAA;QAED,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAA;QAC/C,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,cAAc,CAAC,aAAa,GAAG;IAC7B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;KACd;IACD;QACE,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;KACjB;IACD;QACE,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,kBAAkB;KAC1B;CACF,CAAA;AAED,4BAAY,CAAC,mBAAmB,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAA","sourcesContent":["import { TaskRegistry } from '../task-registry'\nimport { executeHeadlessRequestWithRecovery } from './utils/headless-request-with-recovery'\n\nasync function HeadlessDelete(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, path, queryParams } = stepOptions || {}\n\n try {\n const result = await executeHeadlessRequestWithRecovery(\n connectionName,\n {\n method: 'DELETE',\n path,\n headers: requestHeaders,\n queryParams,\n maxRetries: 2\n },\n { logger, data, domain }\n )\n\n return {\n data: result.data\n }\n } catch (error) {\n logger.error('Error in HeadlessDelete:', error)\n throw error\n }\n}\n\nHeadlessDelete.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'queryParams',\n label: 'query-parameters'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-delete', HeadlessDelete)"]}
@@ -1,72 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const url_1 = require("url");
4
3
  const task_registry_1 = require("../task-registry");
5
- const connection_manager_1 = require("../connection-manager");
4
+ const headless_request_with_recovery_1 = require("./utils/headless-request-with-recovery");
6
5
  async function HeadlessGet(step, { logger, data, domain }) {
7
6
  const { connection: connectionName, params: stepOptions } = step;
8
7
  const { headers: requestHeaders, path, queryParams } = stepOptions || {};
9
- const connection = await connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
10
- if (!connection) {
11
- throw new Error(`Connection '${connectionName}' is not established.`);
12
- }
13
- const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection;
14
- const headers = {
15
- ...requestHeaders
16
- };
17
- const page = await acquireSessionPage();
18
8
  try {
19
- page.on('console', async (msg) => {
20
- console.log(`[browser ${msg.type()}] ${msg.text()}`);
21
- });
22
- page.on('requestfailed', request => {
23
- console.log('Request failed:', request.url());
24
- });
25
- // URL 구성 - queryParams가 있으면 추가
26
- let requestUrl = new url_1.URL(path, endpoint);
27
- if (queryParams && typeof queryParams === 'object') {
28
- Object.keys(queryParams).forEach(key => {
29
- if (queryParams[key] !== null && queryParams[key] !== undefined) {
30
- requestUrl.searchParams.append(key, String(queryParams[key]));
31
- }
32
- });
33
- }
34
- // headless-scrap과 동일한 방식으로 페이지 이동하여 세션 유지
35
- await page.goto(requestUrl, { waitUntil: 'networkidle2' });
36
- // 페이지 내에서 GET 요청 실행 (세션 유지)
37
- const response = await page.evaluate(async (urlString, headers) => {
38
- const response = await fetch(urlString, {
39
- method: 'GET',
40
- headers,
41
- credentials: 'include' // 세션 쿠키/인증 정보 포함
42
- });
43
- if (!response.ok) {
44
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
45
- }
46
- const contentType = response.headers.get('content-type') || '';
47
- if (contentType.includes('application/json')) {
48
- return await response.json();
49
- }
50
- else if (contentType.includes('text/')) {
51
- return await response.text();
52
- }
53
- else {
54
- // Binary data나 기타 타입의 경우
55
- const arrayBuffer = await response.arrayBuffer();
56
- return Array.from(new Uint8Array(arrayBuffer));
57
- }
58
- }, requestUrl.toString(), headers);
9
+ const result = await (0, headless_request_with_recovery_1.executeHeadlessRequestWithRecovery)(connectionName, {
10
+ method: 'GET',
11
+ path,
12
+ headers: requestHeaders,
13
+ queryParams,
14
+ maxRetries: 2
15
+ }, { logger, data, domain });
59
16
  return {
60
- data: response
17
+ data: result.data
61
18
  };
62
19
  }
63
20
  catch (error) {
64
21
  logger.error('Error in HeadlessGet:', error);
65
22
  throw error;
66
23
  }
67
- finally {
68
- await releasePage(page);
69
- }
70
24
  }
71
25
  HeadlessGet.parameterSpec = [
72
26
  {
@@ -1 +1 @@
1
- {"version":3,"file":"headless-get.js","sourceRoot":"","sources":["../../../server/engine/task/headless-get.ts"],"names":[],"mappings":";;AAAA,6BAAyB;AAEzB,oDAA+C;AAC/C,8DAAyD;AAEzD,KAAK,UAAU,WAAW,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IACvD,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IAChE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,IAAI,EAAE,CAAA;IAExE,MAAM,UAAU,GAAG,MAAM,sCAAiB,CAAC,2BAA2B,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAE9F,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,eAAe,cAAc,uBAAuB,CAAC,CAAA;IACvE,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,WAAW,EAAE,GAAG,UAAU,CAAA;IAE1F,MAAM,OAAO,GAAG;QACd,GAAG,cAAc;KAClB,CAAA;IAED,MAAM,IAAI,GAAG,MAAM,kBAAkB,EAAE,CAAA;IAEvC,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE;YAC7B,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QACtD,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE;YACjC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;QAEF,+BAA+B;QAC/B,IAAI,UAAU,GAAG,IAAI,SAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACxC,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACnD,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;gBACrC,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;oBAChE,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;gBAC/D,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,0CAA0C;QAC1C,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAA;QAE1D,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAClC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;YAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;gBACtC,MAAM,EAAE,KAAK;gBACb,OAAO;gBACP,WAAW,EAAE,SAAS,CAAC,iBAAiB;aACzC,CAAC,CAAA;YAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YACpE,CAAC;YAED,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAA;YAE9D,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC7C,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC9B,CAAC;iBAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzC,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC9B,CAAC;iBAAM,CAAC;gBACN,yBAAyB;gBACzB,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAA;gBAChD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAA;YAChD,CAAC;QACH,CAAC,EACD,UAAU,CAAC,QAAQ,EAAE,EACrB,OAAO,CACR,CAAA;QAED,OAAO;YACL,IAAI,EAAE,QAAQ;SACf,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAA;QAC5C,MAAM,KAAK,CAAA;IACb,CAAC;YAAS,CAAC;QACT,MAAM,WAAW,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;AACH,CAAC;AAED,WAAW,CAAC,aAAa,GAAG;IAC1B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;KACd;IACD;QACE,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;KACjB;IACD;QACE,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,kBAAkB;KAC1B;CACF,CAAA;AAED,4BAAY,CAAC,mBAAmB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA","sourcesContent":["import { URL } from 'url'\n\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nasync function HeadlessGet(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, path, queryParams } = stepOptions || {}\n\n const connection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection\n\n const headers = {\n ...requestHeaders\n }\n\n const page = await acquireSessionPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n // URL 구성 - queryParams가 있으면 추가\n let requestUrl = new URL(path, endpoint)\n if (queryParams && typeof queryParams === 'object') {\n Object.keys(queryParams).forEach(key => {\n if (queryParams[key] !== null && queryParams[key] !== undefined) {\n requestUrl.searchParams.append(key, String(queryParams[key]))\n }\n })\n }\n\n // headless-scrap과 동일한 방식으로 페이지 이동하여 세션 유지\n await page.goto(requestUrl, { waitUntil: 'networkidle2' })\n\n // 페이지 내에서 GET 요청 실행 (세션 유지)\n const response = await page.evaluate(\n async (urlString, headers) => {\n const response = await fetch(urlString, {\n method: 'GET',\n headers,\n credentials: 'include' // 세션 쿠키/인증 정보 포함\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const contentType = response.headers.get('content-type') || ''\n\n if (contentType.includes('application/json')) {\n return await response.json()\n } else if (contentType.includes('text/')) {\n return await response.text()\n } else {\n // Binary data 기타 타입의 경우\n const arrayBuffer = await response.arrayBuffer()\n return Array.from(new Uint8Array(arrayBuffer))\n }\n },\n requestUrl.toString(),\n headers\n )\n\n return {\n data: response\n }\n } catch (error) {\n logger.error('Error in HeadlessGet:', error)\n throw error\n } finally {\n await releasePage(page)\n }\n}\n\nHeadlessGet.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'queryParams',\n label: 'query-parameters'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-get', HeadlessGet)\n"]}
1
+ {"version":3,"file":"headless-get.js","sourceRoot":"","sources":["../../../server/engine/task/headless-get.ts"],"names":[],"mappings":";;AAAA,oDAA+C;AAC/C,2FAA2F;AAE3F,KAAK,UAAU,WAAW,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IACvD,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IAChE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,IAAI,EAAE,CAAA;IAExE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,mEAAkC,EACrD,cAAc,EACd;YACE,MAAM,EAAE,KAAK;YACb,IAAI;YACJ,OAAO,EAAE,cAAc;YACvB,WAAW;YACX,UAAU,EAAE,CAAC;SACd,EACD,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CACzB,CAAA;QAED,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAA;QAC5C,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,WAAW,CAAC,aAAa,GAAG;IAC1B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;KACd;IACD;QACE,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;KACjB;IACD;QACE,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,kBAAkB;KAC1B;CACF,CAAA;AAED,4BAAY,CAAC,mBAAmB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA","sourcesContent":["import { TaskRegistry } from '../task-registry'\nimport { executeHeadlessRequestWithRecovery } from './utils/headless-request-with-recovery'\n\nasync function HeadlessGet(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, path, queryParams } = stepOptions || {}\n\n try {\n const result = await executeHeadlessRequestWithRecovery(\n connectionName,\n {\n method: 'GET',\n path,\n headers: requestHeaders,\n queryParams,\n maxRetries: 2\n },\n { logger, data, domain }\n )\n\n return {\n data: result.data\n }\n } catch (error) {\n logger.error('Error in HeadlessGet:', error)\n throw error\n }\n}\n\nHeadlessGet.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'queryParams',\n label: 'query-parameters'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-get', HeadlessGet)\n"]}
@@ -1,91 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const url_1 = require("url");
4
- const utils_1 = require("@things-factory/utils");
5
3
  const task_registry_1 = require("../task-registry");
6
- const connection_manager_1 = require("../connection-manager");
4
+ const headless_request_with_recovery_1 = require("./utils/headless-request-with-recovery");
7
5
  async function HeadlessPatch(step, { logger, data, domain }) {
8
6
  const { connection: connectionName, params: stepOptions } = step;
9
7
  const { headers: requestHeaders, contentType, path, accessor, queryParams } = stepOptions || {};
10
- const connection = await connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
11
- if (!connection) {
12
- throw new Error(`Connection '${connectionName}' is not established.`);
13
- }
14
- const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection;
15
- const headers = {
16
- ...requestHeaders
17
- };
18
- let body = (0, utils_1.access)(accessor, data);
19
- if (contentType && body) {
20
- headers['content-type'] = contentType;
21
- switch (contentType) {
22
- case 'text/plain':
23
- body = JSON.stringify(body);
24
- break;
25
- case 'application/json':
26
- body = JSON.stringify(body);
27
- break;
28
- case 'application/x-www-form-urlencoded':
29
- const searchParams = new URLSearchParams();
30
- for (const prop in body) {
31
- searchParams.set(prop, body[prop]);
32
- }
33
- body = searchParams.toString();
34
- break;
35
- }
36
- }
37
- const page = await acquireSessionPage();
38
8
  try {
39
- page.on('console', async (msg) => {
40
- console.log(`[browser ${msg.type()}] ${msg.text()}`);
41
- });
42
- page.on('requestfailed', request => {
43
- console.log('Request failed:', request.url());
44
- });
45
- // URL 구성 - queryParams가 있으면 추가
46
- let requestUrl = new url_1.URL(path, endpoint);
47
- if (queryParams && typeof queryParams === 'object') {
48
- Object.keys(queryParams).forEach(key => {
49
- if (queryParams[key] !== null && queryParams[key] !== undefined) {
50
- requestUrl.searchParams.append(key, String(queryParams[key]));
51
- }
52
- });
53
- }
54
- // headless-scrap과 동일한 방식으로 페이지 이동하여 세션 유지
55
- await page.goto(requestUrl, { waitUntil: 'networkidle2' });
56
- // 페이지 내에서 PATCH 요청 실행 (세션 유지)
57
- const response = await page.evaluate(async (urlString, options) => {
58
- const response = await fetch(urlString, {
59
- ...options,
60
- credentials: 'include' // 세션 쿠키/인증 정보 포함
61
- });
62
- if (!response.ok) {
63
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
64
- }
65
- const contentType = response.headers.get('content-type') || '';
66
- if (contentType.includes('application/json')) {
67
- return await response.json();
68
- }
69
- else if (contentType.includes('text/')) {
70
- return await response.text();
71
- }
72
- else {
73
- // Binary data나 기타 타입의 경우
74
- const arrayBuffer = await response.arrayBuffer();
75
- return Array.from(new Uint8Array(arrayBuffer));
76
- }
77
- }, requestUrl.toString(), { method: 'PATCH', headers, body });
9
+ const result = await (0, headless_request_with_recovery_1.executeHeadlessRequestWithRecovery)(connectionName, {
10
+ method: 'PATCH',
11
+ path,
12
+ headers: requestHeaders,
13
+ queryParams,
14
+ maxRetries: 2,
15
+ accessor,
16
+ contentType
17
+ }, { logger, data, domain });
78
18
  return {
79
- data: response
19
+ data: result.data
80
20
  };
81
21
  }
82
22
  catch (error) {
83
23
  logger.error('Error in HeadlessPatch:', error);
84
24
  throw error;
85
25
  }
86
- finally {
87
- await releasePage(page);
88
- }
89
26
  }
90
27
  HeadlessPatch.parameterSpec = [
91
28
  {
@@ -1 +1 @@
1
- {"version":3,"file":"headless-patch.js","sourceRoot":"","sources":["../../../server/engine/task/headless-patch.ts"],"names":[],"mappings":"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","sourcesContent":["import { URL } from 'url'\n\nimport { access } from '@things-factory/utils'\nimport { TaskRegistry } from '../task-registry'\nimport { ConnectionManager } from '../connection-manager'\n\nasync function HeadlessPatch(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, contentType, path, accessor, queryParams } = stepOptions || {}\n\n const connection = await ConnectionManager.getConnectionInstanceByName(domain, connectionName)\n\n if (!connection) {\n throw new Error(`Connection '${connectionName}' is not established.`)\n }\n\n const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection\n\n const headers = {\n ...requestHeaders\n }\n\n let body = access(accessor, data)\n if (contentType && body) {\n headers['content-type'] = contentType\n switch (contentType) {\n case 'text/plain':\n body = JSON.stringify(body)\n break\n case 'application/json':\n body = JSON.stringify(body)\n break\n case 'application/x-www-form-urlencoded':\n const searchParams = new URLSearchParams()\n for (const prop in body) {\n searchParams.set(prop, body[prop])\n }\n body = searchParams.toString()\n break\n }\n }\n\n const page = await acquireSessionPage()\n\n try {\n page.on('console', async msg => {\n console.log(`[browser ${msg.type()}] ${msg.text()}`)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n // URL 구성 - queryParams가 있으면 추가\n let requestUrl = new URL(path, endpoint)\n if (queryParams && typeof queryParams === 'object') {\n Object.keys(queryParams).forEach(key => {\n if (queryParams[key] !== null && queryParams[key] !== undefined) {\n requestUrl.searchParams.append(key, String(queryParams[key]))\n }\n })\n }\n\n // headless-scrap과 동일한 방식으로 페이지 이동하여 세션 유지\n await page.goto(requestUrl, { waitUntil: 'networkidle2' })\n\n // 페이지 내에서 PATCH 요청 실행 (세션 유지)\n const response = await page.evaluate(\n async (urlString, options) => {\n const response = await fetch(urlString, {\n ...options,\n credentials: 'include' // 세션 쿠키/인증 정보 포함\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const contentType = response.headers.get('content-type') || ''\n\n if (contentType.includes('application/json')) {\n return await response.json()\n } else if (contentType.includes('text/')) {\n return await response.text()\n } else {\n // Binary data나 기타 타입의 경우\n const arrayBuffer = await response.arrayBuffer()\n return Array.from(new Uint8Array(arrayBuffer))\n }\n },\n requestUrl.toString(),\n { method: 'PATCH', headers, body }\n )\n\n return {\n data: response\n }\n } catch (error) {\n logger.error('Error in HeadlessPatch:', error)\n throw error\n } finally {\n await releasePage(page)\n }\n}\n\nHeadlessPatch.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'queryParams',\n label: 'query-parameters'\n },\n {\n type: 'select',\n name: 'contentType',\n label: 'content-type',\n property: {\n options: [\n { display: '', value: '' },\n { display: 'application/json', value: 'application/json' },\n { display: 'text/plain', value: 'text/plain' },\n { display: 'application/x-www-form-urlencoded', value: 'application/x-www-form-urlencoded' }\n ]\n }\n },\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-patch', HeadlessPatch)\n"]}
1
+ {"version":3,"file":"headless-patch.js","sourceRoot":"","sources":["../../../server/engine/task/headless-patch.ts"],"names":[],"mappings":";;AAAA,oDAA+C;AAC/C,2FAA2F;AAE3F,KAAK,UAAU,aAAa,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IACzD,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IAChE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,IAAI,EAAE,CAAA;IAE/F,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,mEAAkC,EACrD,cAAc,EACd;YACE,MAAM,EAAE,OAAO;YACf,IAAI;YACJ,OAAO,EAAE,cAAc;YACvB,WAAW;YACX,UAAU,EAAE,CAAC;YACb,QAAQ;YACR,WAAW;SACZ,EACD,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CACzB,CAAA;QAED,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAA;QAC9C,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,aAAa,CAAC,aAAa,GAAG;IAC5B;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;KACd;IACD;QACE,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;KACjB;IACD;QACE,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,kBAAkB;KAC1B;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,cAAc;QACrB,QAAQ,EAAE;YACR,OAAO,EAAE;gBACP,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC1B,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,EAAE;gBAC1D,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE;gBAC9C,EAAE,OAAO,EAAE,mCAAmC,EAAE,KAAK,EAAE,mCAAmC,EAAE;aAC7F;SACF;KACF;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;KAClB;CACF,CAAA;AAED,4BAAY,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAA","sourcesContent":["import { TaskRegistry } from '../task-registry'\nimport { executeHeadlessRequestWithRecovery } from './utils/headless-request-with-recovery'\n\nasync function HeadlessPatch(step, { logger, data, domain }) {\n const { connection: connectionName, params: stepOptions } = step\n const { headers: requestHeaders, contentType, path, accessor, queryParams } = stepOptions || {}\n\n try {\n const result = await executeHeadlessRequestWithRecovery(\n connectionName,\n {\n method: 'PATCH',\n path,\n headers: requestHeaders,\n queryParams,\n maxRetries: 2,\n accessor,\n contentType\n },\n { logger, data, domain }\n )\n\n return {\n data: result.data\n }\n } catch (error) {\n logger.error('Error in HeadlessPatch:', error)\n throw error\n }\n}\n\nHeadlessPatch.parameterSpec = [\n {\n type: 'string',\n name: 'path',\n label: 'path'\n },\n {\n type: 'http-headers',\n name: 'headers',\n label: 'headers'\n },\n {\n type: 'options',\n name: 'queryParams',\n label: 'query-parameters'\n },\n {\n type: 'select',\n name: 'contentType',\n label: 'content-type',\n property: {\n options: [\n { display: '', value: '' },\n { display: 'application/json', value: 'application/json' },\n { display: 'text/plain', value: 'text/plain' },\n { display: 'application/x-www-form-urlencoded', value: 'application/x-www-form-urlencoded' }\n ]\n }\n },\n {\n type: 'scenario-step-input',\n name: 'accessor',\n label: 'accessor'\n }\n]\n\nTaskRegistry.registerTaskHandler('headless-patch', HeadlessPatch)\n"]}
@@ -1,91 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const url_1 = require("url");
4
- const utils_1 = require("@things-factory/utils");
5
3
  const task_registry_1 = require("../task-registry");
6
- const connection_manager_1 = require("../connection-manager");
4
+ const headless_request_with_recovery_1 = require("./utils/headless-request-with-recovery");
7
5
  async function HeadlessPost(step, { logger, data, domain }) {
8
6
  const { connection: connectionName, params: stepOptions } = step;
9
7
  const { headers: requestHeaders, contentType, path, accessor, queryParams } = stepOptions || {};
10
- const connection = await connection_manager_1.ConnectionManager.getConnectionInstanceByName(domain, connectionName);
11
- if (!connection) {
12
- throw new Error(`Connection '${connectionName}' is not established.`);
13
- }
14
- const { endpoint, params: connectionParams, acquireSessionPage, releasePage } = connection;
15
- const headers = {
16
- ...requestHeaders
17
- };
18
- let body = (0, utils_1.access)(accessor, data);
19
- if (contentType && body) {
20
- headers['content-type'] = contentType;
21
- switch (contentType) {
22
- case 'text/plain':
23
- body = JSON.stringify(body);
24
- break;
25
- case 'application/json':
26
- body = JSON.stringify(body);
27
- break;
28
- case 'application/x-www-form-urlencoded':
29
- const searchParams = new URLSearchParams();
30
- for (const prop in body) {
31
- searchParams.set(prop, body[prop]);
32
- }
33
- body = searchParams.toString();
34
- break;
35
- }
36
- }
37
- const page = await acquireSessionPage();
38
8
  try {
39
- page.on('console', async (msg) => {
40
- console.log(`[browser ${msg.type()}] ${msg.text()}`);
41
- });
42
- page.on('requestfailed', request => {
43
- console.log('Request failed:', request.url());
44
- });
45
- // URL 구성 - queryParams가 있으면 추가
46
- let requestUrl = new url_1.URL(path, endpoint);
47
- if (queryParams && typeof queryParams === 'object') {
48
- Object.keys(queryParams).forEach(key => {
49
- if (queryParams[key] !== null && queryParams[key] !== undefined) {
50
- requestUrl.searchParams.append(key, String(queryParams[key]));
51
- }
52
- });
53
- }
54
- // headless-scrap과 동일한 방식으로 페이지 이동하여 세션 유지
55
- await page.goto(requestUrl, { waitUntil: 'networkidle2' });
56
- // 페이지 내에서 POST 요청 실행 (세션 유지)
57
- const response = await page.evaluate(async (urlString, options) => {
58
- const response = await fetch(urlString, {
59
- ...options,
60
- credentials: 'include' // 세션 쿠키/인증 정보 포함
61
- });
62
- if (!response.ok) {
63
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
64
- }
65
- const contentType = response.headers.get('content-type') || '';
66
- if (contentType.includes('application/json')) {
67
- return await response.json();
68
- }
69
- else if (contentType.includes('text/')) {
70
- return await response.text();
71
- }
72
- else {
73
- // Binary data나 기타 타입의 경우
74
- const arrayBuffer = await response.arrayBuffer();
75
- return Array.from(new Uint8Array(arrayBuffer));
76
- }
77
- }, requestUrl.toString(), { method: 'POST', headers, body });
9
+ const result = await (0, headless_request_with_recovery_1.executeHeadlessRequestWithRecovery)(connectionName, {
10
+ method: 'POST',
11
+ path,
12
+ headers: requestHeaders,
13
+ queryParams,
14
+ maxRetries: 2,
15
+ accessor,
16
+ contentType
17
+ }, { logger, data, domain });
78
18
  return {
79
- data: response
19
+ data: result.data
80
20
  };
81
21
  }
82
22
  catch (error) {
83
23
  logger.error('Error in HeadlessPost:', error);
84
24
  throw error;
85
25
  }
86
- finally {
87
- await releasePage(page);
88
- }
89
26
  }
90
27
  HeadlessPost.parameterSpec = [
91
28
  {