@things-factory/integration-base 10.0.0-beta.10 β†’ 10.0.0-beta.108

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 (70) hide show
  1. package/dist-server/controllers/scenario-controller.js +8 -6
  2. package/dist-server/controllers/scenario-controller.js.map +1 -1
  3. package/dist-server/engine/connection-manager.d.ts +19 -0
  4. package/dist-server/engine/connection-manager.js +148 -16
  5. package/dist-server/engine/connection-manager.js.map +1 -1
  6. package/dist-server/engine/connector/headless-connector.d.ts +12 -0
  7. package/dist-server/engine/connector/headless-connector.js +74 -31
  8. package/dist-server/engine/connector/headless-connector.js.map +1 -1
  9. package/dist-server/engine/connector/operato-connector.js +17 -6
  10. package/dist-server/engine/connector/operato-connector.js.map +1 -1
  11. package/dist-server/engine/connector/socket-server.js +12 -2
  12. package/dist-server/engine/connector/socket-server.js.map +1 -1
  13. package/dist-server/engine/evaluate-template.d.ts +22 -3
  14. package/dist-server/engine/evaluate-template.js +43 -4
  15. package/dist-server/engine/evaluate-template.js.map +1 -1
  16. package/dist-server/engine/task/script.js +25 -17
  17. package/dist-server/engine/task/script.js.map +1 -1
  18. package/dist-server/engine/task/socket-listener.js +29 -9
  19. package/dist-server/engine/task/socket-listener.js.map +1 -1
  20. package/dist-server/engine/task/utils/headless-request-with-recovery.js +29 -26
  21. package/dist-server/engine/task/utils/headless-request-with-recovery.js.map +1 -1
  22. package/dist-server/engine/types.d.ts +32 -0
  23. package/dist-server/engine/types.js.map +1 -1
  24. package/dist-server/service/connection/connection-mutation.d.ts +3 -0
  25. package/dist-server/service/connection/connection-mutation.js +25 -26
  26. package/dist-server/service/connection/connection-mutation.js.map +1 -1
  27. package/dist-server/service/connection/connection-query.js +11 -3
  28. package/dist-server/service/connection/connection-query.js.map +1 -1
  29. package/dist-server/service/connection/connection-subscription.js +13 -6
  30. package/dist-server/service/connection/connection-subscription.js.map +1 -1
  31. package/dist-server/service/connection/connection-type.d.ts +4 -1
  32. package/dist-server/service/connection/connection-type.js +21 -0
  33. package/dist-server/service/connection/connection-type.js.map +1 -1
  34. package/dist-server/service/connection/connection.d.ts +80 -2
  35. package/dist-server/service/connection/connection.js +59 -15
  36. package/dist-server/service/connection/connection.js.map +1 -1
  37. package/dist-server/service/domain-attribute/domain-attribute-query.d.ts +17 -0
  38. package/dist-server/service/domain-attribute/domain-attribute-query.js +76 -0
  39. package/dist-server/service/domain-attribute/domain-attribute-query.js.map +1 -0
  40. package/dist-server/service/domain-attribute/domain-attribute-type.d.ts +15 -0
  41. package/dist-server/service/domain-attribute/domain-attribute-type.js +46 -0
  42. package/dist-server/service/domain-attribute/domain-attribute-type.js.map +1 -0
  43. package/dist-server/service/domain-attribute/index.d.ts +3 -0
  44. package/dist-server/service/domain-attribute/index.js +8 -0
  45. package/dist-server/service/domain-attribute/index.js.map +1 -0
  46. package/dist-server/service/index.d.ts +1 -1
  47. package/dist-server/service/index.js +3 -1
  48. package/dist-server/service/index.js.map +1 -1
  49. package/dist-server/service/scenario/scenario-mutation.js +10 -0
  50. package/dist-server/service/scenario/scenario-mutation.js.map +1 -1
  51. package/dist-server/service/scenario/scenario-query.d.ts +3 -3
  52. package/dist-server/service/scenario/scenario-query.js +18 -11
  53. package/dist-server/service/scenario/scenario-query.js.map +1 -1
  54. package/dist-server/service/scenario-instance/scenario-instance-subscription.js +4 -4
  55. package/dist-server/service/scenario-instance/scenario-instance-subscription.js.map +1 -1
  56. package/dist-server/service/scenario-instance/scenario-instance-type.js +79 -0
  57. package/dist-server/service/scenario-instance/scenario-instance-type.js.map +1 -1
  58. package/dist-server/service/scenario-queue/scenario-queue-subscription.js +2 -2
  59. package/dist-server/service/scenario-queue/scenario-queue-subscription.js.map +1 -1
  60. package/dist-server/service/state-register/data-resolver.js +2 -2
  61. package/dist-server/service/state-register/data-resolver.js.map +1 -1
  62. package/dist-server/service/step/step-mutation.js +15 -0
  63. package/dist-server/service/step/step-mutation.js.map +1 -1
  64. package/dist-server/service/step/step-query.js +11 -2
  65. package/dist-server/service/step/step-query.js.map +1 -1
  66. package/dist-server/tsconfig.tsbuildinfo +1 -1
  67. package/dist-server/utils/domain-inheritance.d.ts +29 -0
  68. package/dist-server/utils/domain-inheritance.js +98 -0
  69. package/dist-server/utils/domain-inheritance.js.map +1 -0
  70. package/package.json +10 -10
@@ -9,18 +9,20 @@ const auth_base_1 = require("@things-factory/auth-base");
9
9
  const cache_service_1 = require("@things-factory/cache-service");
10
10
  const scenario_engine_1 = require("../engine/scenario-engine");
11
11
  const scenario_1 = require("../service/scenario/scenario");
12
+ const domain_inheritance_1 = require("../utils/domain-inheritance");
12
13
  const debug = require('debug')('things-factory:integration-base:controller:run-scenario');
13
14
  async function findScenario(scenarioName, domain) {
14
15
  var repository = (0, shell_1.getRepository)(scenario_1.Scenario);
15
- var scenario = await repository.findOne({
16
- where: { domain: { id: domain.id }, name: scenarioName },
17
- relations: ['domain', 'steps', 'role', 'creator', 'updater']
18
- });
19
- if (!scenario && domain.parentId) {
16
+ // ancestor 도메인을 closest-first 둜 μˆœνšŒν•˜μ—¬ first hit μ‚¬μš©
17
+ const domainIds = await (0, domain_inheritance_1.getDomainIdsWithAncestors)(domain);
18
+ var scenario = null;
19
+ for (const did of domainIds) {
20
20
  scenario = await repository.findOne({
21
- where: { domain: { id: domain.parentId }, name: scenarioName },
21
+ where: { domain: { id: did }, name: scenarioName },
22
22
  relations: ['domain', 'steps', 'role', 'creator', 'updater']
23
23
  });
24
+ if (scenario)
25
+ break;
24
26
  }
25
27
  return scenario;
26
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scenario-controller.js","sourceRoot":"","sources":["../../server/controllers/scenario-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository, Domain, GraphqlLocalClient } from '@things-factory/shell'\nimport { checkUserHasRole } from '@things-factory/auth-base'\nimport { cacheService } from '@things-factory/cache-service'\n\nimport { ScenarioEngine } from '../engine/scenario-engine'\nimport { Scenario } from '../service/scenario/scenario'\nimport {\n ScenarioInstance,\n ScenarioInstanceRunResult,\n ScenarioInstanceStartResult\n} from '../service/scenario-instance/scenario-instance-type'\nimport { Step } from '../service/step/step-type'\n\nconst debug = require('debug')('things-factory:integration-base:controller:run-scenario')\n\nasync function findScenario(\n scenarioName: string,\n domain: Domain\n): Promise<{\n id: string\n ttl: number\n name: string\n steps: Step[]\n domain: Domain\n}> {\n var repository = getRepository(Scenario)\n\n var scenario = await repository.findOne({\n where: { domain: { id: domain.id }, name: scenarioName },\n relations: ['domain', 'steps', 'role', 'creator', 'updater']\n })\n\n if (!scenario && domain.parentId) {\n scenario = await repository.findOne({\n where: { domain: { id: domain.parentId }, name: scenarioName },\n relations: ['domain', 'steps', 'role', 'creator', 'updater']\n })\n }\n\n return scenario as any\n}\n\nexport async function checkHasRole(scenario: Partial<Scenario>, context: ResolverContext): Promise<void> {\n const { domain, user } = context.state\n if (!(await checkUserHasRole(scenario.roleId, domain, user))) {\n throw new Error(\n context.t('error.scenario run unauthorized', {\n scenario: scenario.name\n })\n )\n }\n}\n\nexport async function runScenario(\n instanceName: string,\n scenarioName: string,\n variables: any,\n context: ResolverContext\n): Promise<ScenarioInstanceRunResult> {\n const { domain, user, lng } = context.state\n\n debug('runScenario', scenarioName, instanceName, variables)\n\n var scenario = await findScenario(scenarioName, domain)\n\n if (!scenario) {\n throw new Error(\n context.t('error.scenario not found', {\n scenario: scenarioName\n })\n )\n }\n\n await checkHasRole(scenario, context)\n\n if (scenario.ttl > 0) {\n const cachedValue = await cacheService.getFromCache(scenario.id, { domain: domain.id, variables: variables || {} })\n if (cachedValue) {\n return cachedValue.value\n }\n }\n\n /* μ‹œλ‚˜λ¦¬μ˜€ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•œλ‹€. */\n instanceName = instanceName || scenarioName + '-' + String(Date.now())\n\n try {\n return await ScenarioEngine.run(instanceName, scenario, { domain, user, lng, variables })\n } catch (err) {\n console.error(err)\n\n throw new Error(\n context.t('error.scenario run error', {\n scenario: scenarioName\n })\n )\n }\n}\n\nexport async function startScenario(\n instanceName: string,\n scenarioName: string,\n variables: any,\n context: ResolverContext\n): Promise<ScenarioInstanceStartResult> {\n const { domain, user, lng } = context.state\n\n debug('startScenario', instanceName, scenarioName, variables)\n\n var scenario = await findScenario(scenarioName, domain)\n\n if (!scenario) {\n throw new Error(\n context.t('error.scenario not found', {\n scenario: scenarioName\n })\n )\n }\n\n await checkHasRole(scenario, context)\n\n instanceName = instanceName || scenarioName\n return await ScenarioEngine.loadIteration(instanceName, scenario, { domain, user, lng, variables })\n}\n\nexport async function stopScenario(\n instanceName: string,\n context: ResolverContext\n): Promise<ScenarioInstance | undefined> {\n const { domain, user, unsafeIP, prohibitedPrivileges } = context.state\n\n debug('stopScenario', instanceName)\n\n var scenarioInstance = ScenarioEngine.getScenarioInstance(domain, instanceName)\n\n if (!scenarioInstance) {\n debug('stopScenario', `ScenarioInstance(${instanceName}) Not Found.`)\n throw new Error(\n context.t('error.scenario instance not found', {\n instance: instanceName\n })\n )\n }\n\n var scenario = await findScenario(scenarioInstance.scenarioName, domain)\n\n await checkHasRole(scenario, context)\n\n await ScenarioEngine.unload(domain, instanceName)\n\n return scenarioInstance\n}\n"]}
1
+ {"version":3,"file":"scenario-controller.js","sourceRoot":"","sources":["../../server/controllers/scenario-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository, Domain, GraphqlLocalClient } from '@things-factory/shell'\nimport { checkUserHasRole } from '@things-factory/auth-base'\nimport { cacheService } from '@things-factory/cache-service'\n\nimport { ScenarioEngine } from '../engine/scenario-engine'\nimport { Scenario } from '../service/scenario/scenario'\nimport { getDomainIdsWithAncestors } from '../utils/domain-inheritance'\nimport {\n ScenarioInstance,\n ScenarioInstanceRunResult,\n ScenarioInstanceStartResult\n} from '../service/scenario-instance/scenario-instance-type'\nimport { Step } from '../service/step/step-type'\n\nconst debug = require('debug')('things-factory:integration-base:controller:run-scenario')\n\nasync function findScenario(\n scenarioName: string,\n domain: Domain\n): Promise<{\n id: string\n ttl: number\n name: string\n steps: Step[]\n domain: Domain\n}> {\n var repository = getRepository(Scenario)\n\n // ancestor 도메인을 closest-first 둜 μˆœνšŒν•˜μ—¬ first hit μ‚¬μš©\n const domainIds = await getDomainIdsWithAncestors(domain)\n var scenario: Scenario | null = null\n for (const did of domainIds) {\n scenario = await repository.findOne({\n where: { domain: { id: did }, name: scenarioName },\n relations: ['domain', 'steps', 'role', 'creator', 'updater']\n })\n if (scenario) break\n }\n\n return scenario as any\n}\n\nexport async function checkHasRole(scenario: Partial<Scenario>, context: ResolverContext): Promise<void> {\n const { domain, user } = context.state\n if (!(await checkUserHasRole(scenario.roleId, domain, user))) {\n throw new Error(\n context.t('error.scenario run unauthorized', {\n scenario: scenario.name\n })\n )\n }\n}\n\nexport async function runScenario(\n instanceName: string,\n scenarioName: string,\n variables: any,\n context: ResolverContext\n): Promise<ScenarioInstanceRunResult> {\n const { domain, user, lng } = context.state\n\n debug('runScenario', scenarioName, instanceName, variables)\n\n var scenario = await findScenario(scenarioName, domain)\n\n if (!scenario) {\n throw new Error(\n context.t('error.scenario not found', {\n scenario: scenarioName\n })\n )\n }\n\n await checkHasRole(scenario, context)\n\n if (scenario.ttl > 0) {\n const cachedValue = await cacheService.getFromCache(scenario.id, { domain: domain.id, variables: variables || {} })\n if (cachedValue) {\n return cachedValue.value\n }\n }\n\n /* μ‹œλ‚˜λ¦¬μ˜€ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•œλ‹€. */\n instanceName = instanceName || scenarioName + '-' + String(Date.now())\n\n try {\n return await ScenarioEngine.run(instanceName, scenario, { domain, user, lng, variables })\n } catch (err) {\n console.error(err)\n\n throw new Error(\n context.t('error.scenario run error', {\n scenario: scenarioName\n })\n )\n }\n}\n\nexport async function startScenario(\n instanceName: string,\n scenarioName: string,\n variables: any,\n context: ResolverContext\n): Promise<ScenarioInstanceStartResult> {\n const { domain, user, lng } = context.state\n\n debug('startScenario', instanceName, scenarioName, variables)\n\n var scenario = await findScenario(scenarioName, domain)\n\n if (!scenario) {\n throw new Error(\n context.t('error.scenario not found', {\n scenario: scenarioName\n })\n )\n }\n\n await checkHasRole(scenario, context)\n\n instanceName = instanceName || scenarioName\n return await ScenarioEngine.loadIteration(instanceName, scenario, { domain, user, lng, variables })\n}\n\nexport async function stopScenario(\n instanceName: string,\n context: ResolverContext\n): Promise<ScenarioInstance | undefined> {\n const { domain, user, unsafeIP, prohibitedPrivileges } = context.state\n\n debug('stopScenario', instanceName)\n\n var scenarioInstance = ScenarioEngine.getScenarioInstance(domain, instanceName)\n\n if (!scenarioInstance) {\n debug('stopScenario', `ScenarioInstance(${instanceName}) Not Found.`)\n throw new Error(\n context.t('error.scenario instance not found', {\n instance: instanceName\n })\n )\n }\n\n var scenario = await findScenario(scenarioInstance.scenarioName, domain)\n\n await checkHasRole(scenario, context)\n\n await ScenarioEngine.unload(domain, instanceName)\n\n return scenarioInstance\n}\n"]}
@@ -5,6 +5,12 @@ export declare class ConnectionManager {
5
5
  private static connectors;
6
6
  private static connections;
7
7
  private static entities;
8
+ /**
9
+ * Session 직렬화 mutex β€” connection name λ‹¨μœ„ (도메인 무관 κΈ€λ‘œλ²Œ).
10
+ * sessionExclusive connector 의 acquireSessionPage λ™μ‹œ 호좜이 μ™ΈλΆ€ μ„Έμ…˜μ„ μ„œλ‘œ
11
+ * invalidate ν•˜μ§€ μ•Šλ„λ‘ νμž‰. release 호좜 μ‹œ λ‹€μŒ λŒ€κΈ°μžκ°€ μ§„ν–‰.
12
+ */
13
+ private static sessionLocks;
8
14
  private static logFormat;
9
15
  static logger: import("winston").Logger;
10
16
  static ready(): Promise<void>;
@@ -21,6 +27,19 @@ export declare class ConnectionManager {
21
27
  };
22
28
  static getEntities(): {};
23
29
  static getConnectionInstance(connection: Connection): any;
30
+ /**
31
+ * μ„Έμ…˜ μ‚¬μš© νŒ¨ν„΄ (RAII).
32
+ *
33
+ * connector κ°€ sessionExclusive=true 둜 μ„ μ–Έν•œ 경우 connection.name λ‹¨μœ„ mutex 둜
34
+ * 직렬화. ν˜ΈμΆœμžλŠ” acquireSessionPage / releasePage 의 lifecycle 을 μ‹ κ²½ μ“Έ ν•„μš” 없이
35
+ * `withSession(connection, async page => { ... })` ν˜•νƒœλ‘œ μ‚¬μš©ν•˜λ©΄ 됨.
36
+ *
37
+ * stateless connector (sessionExclusive=false) λŠ” mutex 없이 μ¦‰μ‹œ μ§„ν–‰.
38
+ *
39
+ * @param connection 도메인 instance (connection.type μ—μ„œ connector 쑰회)
40
+ * @param fn page λ₯Ό λ°›μ•„ 데이터 μž‘μ—…ν•˜λŠ” async ν•¨μˆ˜
41
+ */
42
+ static withSession<T>(connection: any, fn: (page: any) => Promise<T>): Promise<T>;
24
43
  static getConnectionInstanceByName(domain: Domain, name: string): Promise<any>;
25
44
  static getConnectionEntityByName(domain: Domain, name: string): Promise<Connection | null>;
26
45
  static getConnectionInstanceEntityByName(domain: Domain, name: string): any;
@@ -6,6 +6,7 @@ const moment_timezone_1 = tslib_1.__importDefault(require("moment-timezone"));
6
6
  const winston_1 = require("winston");
7
7
  const shell_1 = require("@things-factory/shell");
8
8
  const service_1 = require("../service");
9
+ const domain_inheritance_1 = require("../utils/domain-inheritance");
9
10
  const proxy_connector_1 = require("./connector/proxy-connector");
10
11
  const { combine, splat, printf, errors } = winston_1.format;
11
12
  const debug = require('debug')('things-factory:integration-base:connections');
@@ -32,6 +33,12 @@ class ConnectionManager {
32
33
  static { this.connectors = {}; }
33
34
  static { this.connections = {}; }
34
35
  static { this.entities = {}; }
36
+ /**
37
+ * Session 직렬화 mutex β€” connection name λ‹¨μœ„ (도메인 무관 κΈ€λ‘œλ²Œ).
38
+ * sessionExclusive connector 의 acquireSessionPage λ™μ‹œ 호좜이 μ™ΈλΆ€ μ„Έμ…˜μ„ μ„œλ‘œ
39
+ * invalidate ν•˜μ§€ μ•Šλ„λ‘ νμž‰. release 호좜 μ‹œ λ‹€μŒ λŒ€κΈ°μžκ°€ μ§„ν–‰.
40
+ */
41
+ static { this.sessionLocks = new Map(); }
35
42
  static { this.logFormat = printf(({ level, message, timestamp, stack }) => {
36
43
  return `${timestamp} ${level}: ${stack || message}`;
37
44
  }); }
@@ -114,20 +121,106 @@ class ConnectionManager {
114
121
  const { domain, name } = connection;
115
122
  return ConnectionManager.connections[domain.id]?.[name];
116
123
  }
124
+ /**
125
+ * μ„Έμ…˜ μ‚¬μš© νŒ¨ν„΄ (RAII).
126
+ *
127
+ * connector κ°€ sessionExclusive=true 둜 μ„ μ–Έν•œ 경우 connection.name λ‹¨μœ„ mutex 둜
128
+ * 직렬화. ν˜ΈμΆœμžλŠ” acquireSessionPage / releasePage 의 lifecycle 을 μ‹ κ²½ μ“Έ ν•„μš” 없이
129
+ * `withSession(connection, async page => { ... })` ν˜•νƒœλ‘œ μ‚¬μš©ν•˜λ©΄ 됨.
130
+ *
131
+ * stateless connector (sessionExclusive=false) λŠ” mutex 없이 μ¦‰μ‹œ μ§„ν–‰.
132
+ *
133
+ * @param connection 도메인 instance (connection.type μ—μ„œ connector 쑰회)
134
+ * @param fn page λ₯Ό λ°›μ•„ 데이터 μž‘μ—…ν•˜λŠ” async ν•¨μˆ˜
135
+ */
136
+ static async withSession(connection, fn) {
137
+ const connector = ConnectionManager.getConnector(connection.type);
138
+ const exclusive = !!connector?.sessionExclusive;
139
+ const run = async () => {
140
+ const page = await connection.acquireSessionPage();
141
+ try {
142
+ return await fn(page);
143
+ }
144
+ finally {
145
+ // releasePage κ°€ page 인자λ₯Ό λ°›μŒ β€” λˆ„λ½ μ‹œ λ‚΄λΆ€μ˜ page.close() 와
146
+ // browser ν’€ λ°˜ν™˜μ΄ μŠ€ν‚΅λ˜μ–΄ λΈŒλΌμš°μ €κ°€ leak. ν’€ 고갈 β†’ 정상 μ’…λ£Œ hang 으둜 이어짐.
147
+ try {
148
+ await connection.releasePage?.(page);
149
+ }
150
+ catch (e) {
151
+ ConnectionManager.logger.warn(`releasePage failed for '${connection.name}':`, e);
152
+ }
153
+ }
154
+ };
155
+ if (!exclusive)
156
+ return await run();
157
+ // mutex: connection name λ‹¨μœ„λ‘œ 직렬화 (도메인 무관, 같은 μ™ΈλΆ€ 계정 쀑볡 둜그인 λ°©μ§€)
158
+ const lockKey = connection.name;
159
+ const wasQueued = ConnectionManager.sessionLocks.has(lockKey);
160
+ const prev = ConnectionManager.sessionLocks.get(lockKey) || Promise.resolve();
161
+ let release;
162
+ const next = new Promise(r => (release = r));
163
+ // chain: 이전 μž‘μ—…μ΄ λλ‚œ λ’€ next κ°€ 끝날 λ•ŒκΉŒμ§€ pending β€” λ‹€μŒ λŒ€κΈ°μžκ°€ 이 chain 으둜 await
164
+ const chain = prev.then(() => next);
165
+ ConnectionManager.sessionLocks.set(lockKey, chain);
166
+ // 큐 λŒ€κΈ° 진단 β€” 운영 쀑 "μ™œ μž‘μ—…μ΄ stall λ˜λŠ”κ°€" μΆ”μ μš©
167
+ const waitStartedAt = wasQueued ? Date.now() : 0;
168
+ if (wasQueued) {
169
+ ConnectionManager.logger.info(`[withSession] '${lockKey}' queued behind existing lock β€” waiting (domain='${connection.domain?.subdomain || connection.domainId}')`);
170
+ }
171
+ await prev;
172
+ if (wasQueued) {
173
+ const waited = Date.now() - waitStartedAt;
174
+ ConnectionManager.logger.info(`[withSession] '${lockKey}' lock acquired after ${waited}ms wait`);
175
+ }
176
+ try {
177
+ return await run();
178
+ }
179
+ finally {
180
+ release();
181
+ // λ‚΄κ°€ latest λ©΄ (이 chain λ’€λ‘œ μƒˆλ‘œ λ“€μ–΄μ˜¨ λŒ€κΈ°μž μ—†μŒ) cleanup
182
+ if (ConnectionManager.sessionLocks.get(lockKey) === chain) {
183
+ ConnectionManager.sessionLocks.delete(lockKey);
184
+ }
185
+ }
186
+ }
117
187
  static async getConnectionInstanceByName(domain, name) {
118
188
  const connections = ConnectionManager.connections[domain.id];
119
189
  const connection = connections?.[name];
120
190
  if (!connection) {
121
- // on-demand 컀λ„₯μ…˜μΈμ§€ ν™•μΈν•˜κ³  생성
191
+ // μžμ‹ 도메인에 instance κ°€ 없을 λ•Œμ˜ μ„Έ κ°€μ§€ μžλ™ 처리 λΆ„κΈ°:
192
+ //
193
+ // (a) entity.__sharedFrom 있음 β€” SHARE λͺ¨λ“œ inheritance
194
+ // β†’ λΆ€λͺ¨ λ„λ©”μΈμ˜ instance λ₯Ό κ·ΈλŒ€λ‘œ lookup ν•΄μ„œ λ°˜ν™˜. μžμ‹ λ„λ©”μΈμ—λŠ” instance 등둝 μ•ˆ 함.
195
+ // 같은 instance κ°€ λΆ€λͺ¨Β·μžμ‹ λͺ¨λ‘μ—κ²Œ μ„œλΉ„μŠ€ 됨.
196
+ //
197
+ // (b) entity.__inheritedFrom 있음 β€” ISOLATE λͺ¨λ“œ inheritance
198
+ // β†’ μžμ‹ 도메인 ν‚€ μ•„λž˜ μƒˆ instance μžλ™ 생성. cookies/μ„Έμ…˜ 격리.
199
+ //
200
+ // (c) entity.onDemand=true β€” λͺ…μ‹œμ  lazy 생성 (μžμ‹ 자체 record 인 κ²½μš°μ—λ„ 적용)
201
+ // β†’ μžμ‹ 도메인 ν‚€ μ•„λž˜ μƒˆ instance 생성.
202
+ //
203
+ // λ‚˜λ¨Έμ§€: μžμ‹ record 도 μ—†κ³  inherit 도 μ•ˆ λ˜λŠ” 경우 β†’ throw.
122
204
  try {
123
205
  const connectionEntity = await ConnectionManager.getConnectionEntityByName(domain, name);
124
- if (connectionEntity?.onDemand) {
125
- // on-demand 컀λ„₯μ…˜ 생성
126
- const onDemandInstance = await ConnectionManager.createOnDemandConnection(connectionEntity);
127
- return onDemandInstance;
206
+ if (connectionEntity?.__sharedFrom) {
207
+ // SHARE: λΆ€λͺ¨ instance 직접 μ‚¬μš©
208
+ const parentInstance = ConnectionManager.connections[connectionEntity.__sharedFrom]?.[name];
209
+ if (parentInstance)
210
+ return parentInstance;
211
+ throw `SHARE mode: parent instance for '${name}' (domain id '${connectionEntity.__sharedFrom}') not registered yet β€” startup ready() may have failed for parent`;
212
+ }
213
+ if (connectionEntity?.onDemand || connectionEntity?.__inheritedFrom) {
214
+ const instance = await ConnectionManager.createOnDemandConnection(connectionEntity);
215
+ return instance;
216
+ }
217
+ else if (connectionEntity) {
218
+ // entity λŠ” μ°Ύμ•˜λŠ”λ° instance 생성 trigger (onDemand/inherit) μ—†μŒ β€” μš΄μ˜μžκ°€ record 자체 점검 ν•„μš”
219
+ throw `Connection '${name}' record exists but instance is not registered and no auto-create trigger (onDemand=false, no inheritance marker). Check whether parent record's active=true and ready() succeeded at startup.`;
128
220
  }
129
221
  else {
130
- throw `The connection with the given name(${name}) cannot be found`;
222
+ // entity 자체 λͺ» 찾음 β€” 도메인 트리 어디에도 record μ—†μŒ
223
+ throw `Connection '${name}' not found in domain '${domain.subdomain || domain.id}' or any ancestor β€” verify the Connection record exists at the running domain or any of its ancestors.`;
131
224
  }
132
225
  }
133
226
  catch (error) {
@@ -163,22 +256,61 @@ class ConnectionManager {
163
256
  // return parentCachedEntity
164
257
  // }
165
258
  // }
166
- // 4. λΆ€λͺ¨ λ„λ©”μΈμ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회 (fallback)
167
- if (domain.parentId) {
168
- connection = await (0, shell_1.getRepository)(service_1.Connection).findOne({
259
+ // 4. ancestor 도메인 트리 fallback (closest-first walk)
260
+ //
261
+ // EnvVar inheritance 와 μΌκ΄€λ˜κ²Œ λͺ¨λ“  쑰상 도메인을 closest-first 둜 탐색.
262
+ // 예: SYSTEM β†’ μ‹œκ³΅μ‚¬ β†’ ν”„λ‘œμ νŠΈ νŠΈλ¦¬μ—μ„œ μ‹œλ‚˜λ¦¬μ˜€κ°€ ν”„λ‘œμ νŠΈ λ„λ©”μΈμ—μ„œ 싀행될 λ•Œ
263
+ // Connection record κ°€ SYSTEM λ˜λŠ” μ‹œκ³΅μ‚¬ 어디에 μžˆμ–΄λ„ 찾아냄.
264
+ //
265
+ // 맀칭된 ancestor 의 inheritanceMode 에 따라 두 갈래:
266
+ // ISOLATE (default): μžμ‹ λ„λ©”μΈμš© clone 생성. cookies/μ„Έμ…˜ 격리, μžμ‹ μ»¨ν…μŠ€νŠΈμ—μ„œ
267
+ // EnvVar override ν•΄μ†Œ. __inheritedFrom marker 둜 후속 μžλ™ instance.
268
+ // SHARE: λΆ€λͺ¨ entity κ·ΈλŒ€λ‘œ λ°˜ν™˜. paramsΒ·μ„Έμ…˜Β·μΈμŠ€ν„΄μŠ€ λͺ¨λ‘ λΆ€λͺ¨ 것 곡유.
269
+ // __sharedFrom marker 둜 instance lookup 이 λΆ€λͺ¨ 도메인을 κ°€λ¦¬ν‚€κ²Œ 함.
270
+ //
271
+ // ν˜Έν™˜μ„±: inheritanceMode λ―Έμ§€μ •(NULL) β†’ connector default β†’ 폴백 ISOLATE.
272
+ // κΈ°μ‘΄ λͺ¨λ“  Connection 은 이 column μΆ”κ°€ μ „ λ§Œλ“€μ–΄μ‘ŒμœΌλ―€λ‘œ NULL β†’ ISOLATE = κΈ°μ‘΄ 거동.
273
+ const ancestorIds = await (0, domain_inheritance_1.getDomainIdsWithAncestors)(domain);
274
+ // ancestorIds[0] = domain.id (이미 μœ„ λΆ„κΈ°μ—μ„œ μ‹œλ„), skip
275
+ const searchedAncestors = [];
276
+ for (let i = 1; i < ancestorIds.length; i++) {
277
+ const ancestorId = ancestorIds[i];
278
+ searchedAncestors.push(ancestorId);
279
+ const parentConn = await (0, shell_1.getRepository)(service_1.Connection).findOne({
169
280
  where: {
170
- domain: { id: domain.parentId },
281
+ domain: { id: ancestorId },
171
282
  name: name
172
283
  },
173
284
  relations: ['domain', 'edge', 'creator', 'updater']
174
285
  });
175
- if (connection) {
176
- const resolvedParams = await connection.getResolvedParameters();
177
- connection.params = resolvedParams;
178
- connection.domain = domain;
179
- connection.domainId = domain.id;
180
- return connection;
286
+ if (!parentConn)
287
+ continue; // 이 ancestor 에 μ—†μœΌλ©΄ λ‹€μŒ ancestor
288
+ ConnectionManager.logger.info(`[inheritance] '${name}' inherited by domain '${domain.subdomain || domain.id}' ` +
289
+ `from ancestor '${parentConn.domain?.subdomain || ancestorId}' ` +
290
+ `(walked ${i} ancestor(s))`);
291
+ const connectorDefault = ConnectionManager.connectors[parentConn.type]?.inheritanceMode;
292
+ const effectiveMode = parentConn.inheritanceMode || connectorDefault || service_1.ConnectionInheritanceMode.ISOLATE;
293
+ if (effectiveMode === service_1.ConnectionInheritanceMode.SHARE) {
294
+ // SHARE β€” λΆ€λͺ¨ entity κ·ΈλŒ€λ‘œ λ°˜ν™˜ (mutation μ΅œμ†Œν™”).
295
+ const shared = parentConn;
296
+ shared.__sharedFrom = parentConn.domainId;
297
+ shared.params = await parentConn.getResolvedParameters();
298
+ return shared;
181
299
  }
300
+ // ISOLATE (default) β€” clone + marker
301
+ const inherited = Object.assign(Object.create(Object.getPrototypeOf(parentConn)), parentConn, {
302
+ domain,
303
+ domainId: domain.id,
304
+ cookies: undefined,
305
+ __inheritedFrom: parentConn.domainId
306
+ });
307
+ inherited.params = await inherited.getResolvedParameters();
308
+ return inherited;
309
+ }
310
+ // μ–΄λŠ ancestor μ—μ„œλ„ λͺ» 찾음 β€” μš΄μ˜μžκ°€ μ–΄λ””κΉŒμ§€ μ°Ύμ•˜λŠ”μ§€ μ•Œ 수 μžˆλ„λ‘ 진단 둜그
311
+ if (searchedAncestors.length > 0) {
312
+ ConnectionManager.logger.warn(`[inheritance] '${name}' not found at any ancestor of domain '${domain.subdomain || domain.id}' ` +
313
+ `β€” searched: [${searchedAncestors.join(', ')}]`);
182
314
  }
183
315
  return null;
184
316
  }
@@ -1 +1 @@
1
- {"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../../server/engine/connection-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\nimport { createLogger, format, transports } from 'winston'\n\nimport { Domain, getRepository, pubsub, PubSubLogTransport } from '@things-factory/shell'\n\nimport { Connection, ConnectionStatus } from '../service'\nimport { Connector } from './types'\nimport { ProxyConnector } from './connector/proxy-connector'\n\nconst { combine, splat, printf, errors } = format\nconst debug = require('debug')('things-factory:integration-base:connections')\n\nfunction getSystemTimeZone() {\n try {\n const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone\n if (!timeZone) {\n throw new Error('Unable to resolve timeZone')\n }\n return timeZone\n } catch (e) {\n console.warn('Failed to get system timeZone, falling back to UTC.', e)\n return 'UTC'\n }\n}\n\nconst SYSTEM_TZ = getSystemTimeZone()\nconst systemTimestamp = format((info, opts: { tz?: string }) => {\n if (opts.tz) info.timestamp = moment().tz(opts.tz).format()\n return info\n})\n\nexport class ConnectionManager {\n private static connectors: { [propName: string]: Connector } = {}\n private static connections: { [domainId: string]: { [name: string]: any } } = {}\n private static entities = {}\n private static logFormat = printf(({ level, message, timestamp, stack }) => {\n return `${timestamp} ${level}: ${stack || message}`\n })\n\n public static logger = createLogger({\n format: combine(errors({ stack: true }), systemTimestamp({ tz: SYSTEM_TZ }), splat(), ConnectionManager.logFormat),\n transports: [\n new (transports as any).DailyRotateFile({\n filename: `logs/connections-%DATE%.log`,\n datePattern: 'YYYY-MM-DD-HH',\n zippedArchive: false,\n maxSize: '20m',\n maxFiles: '14d',\n level: 'info'\n }),\n new PubSubLogTransport({\n topic: 'connection-log'\n })\n ]\n })\n\n static async ready() {\n const CONNECTIONS = await Promise.all(\n (\n await getRepository(Connection).find({\n where: { active: true },\n relations: ['domain', 'edge', 'creator', 'updater']\n })\n ).map(async connection => {\n // πŸ” ν•΄κ²°λœ νŒŒλΌλ―Έν„° μ‚¬μš©\n const resolvedParams = await connection.getResolvedParameters()\n\n return {\n ...connection,\n params: resolvedParams\n }\n })\n )\n\n ConnectionManager.logger.info('Initializing ConnectionManager...')\n\n return await Promise.all(\n [...Object.keys(ConnectionManager.connectors), 'proxy-connector'].map(type => {\n const connector = type == 'proxy-connector' ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n\n ConnectionManager.logger.info(`Connector '${type}' started to ready`)\n\n return connector\n .ready(\n CONNECTIONS.filter(connection => {\n if (type == 'proxy-connector') {\n return !!connection.edge\n } else {\n return !connection.edge && connection.type == type\n }\n }) as any\n )\n .catch(error => {\n ConnectionManager.logger.error(error)\n })\n .then(() => {\n ConnectionManager.logger.info(`All connector for '${type}' ready`)\n })\n })\n ).then(() => {\n ConnectionManager.logger.info('ConnectionManager initialization done:')\n Object.keys(ConnectionManager.connections).forEach(key => {\n var connections = ConnectionManager.connections[key]\n ConnectionManager.logger.info('For domain(%s) : %s', key, JSON.stringify(Object.keys(connections)))\n })\n })\n }\n\n static registerConnector(type: string, connector: Connector) {\n ConnectionManager.connectors[type] = connector\n }\n\n static getConnector(type: string): Connector {\n return ConnectionManager.connectors[type]\n }\n\n static getConnectors(): { [connectorName: string]: Connector } {\n return {\n ...ConnectionManager.connectors\n }\n }\n\n static unregisterConnector(type: string) {\n delete ConnectionManager.connectors[type]\n }\n\n static getConnections() {\n return ConnectionManager.connections\n }\n\n static getEntities() {\n return ConnectionManager.entities\n }\n\n static getConnectionInstance(connection: Connection): any {\n const { domain, name } = connection\n return ConnectionManager.connections[domain.id]?.[name]\n }\n\n static async getConnectionInstanceByName(domain: Domain, name: string) {\n const connections = ConnectionManager.connections[domain.id]\n const connection = connections?.[name]\n\n if (!connection) {\n // on-demand 컀λ„₯μ…˜μΈμ§€ ν™•μΈν•˜κ³  생성\n try {\n const connectionEntity = await ConnectionManager.getConnectionEntityByName(domain, name)\n\n if (connectionEntity?.onDemand) {\n // on-demand 컀λ„₯μ…˜ 생성\n const onDemandInstance = await ConnectionManager.createOnDemandConnection(connectionEntity)\n return onDemandInstance\n } else {\n throw `The connection with the given name(${name}) cannot be found`\n }\n } catch (error) {\n throw `The connection with the given name(${name}) cannot be found: ${error}`\n }\n }\n\n return connection\n }\n\n static async getConnectionEntityByName(domain: Domain, name: string): Promise<Connection | null> {\n // 1. ν˜„μž¬ λ„λ©”μΈμ—μ„œ λ©”λͺ¨λ¦¬ 쑰회 (μš°μ„ )\n const cachedEntity = ConnectionManager.entities[domain.id]?.[name]\n if (cachedEntity) {\n return cachedEntity\n }\n\n // 2. ν˜„μž¬ λ„λ©”μΈμ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회 (μš°μ„ )\n try {\n let connection = await getRepository(Connection).findOne({\n where: {\n domain: { id: domain.id },\n name: name\n },\n relations: ['domain', 'edge', 'creator', 'updater']\n })\n\n if (connection) {\n const resolvedParams = await connection.getResolvedParameters()\n connection.params = resolvedParams\n return connection\n }\n\n // 3. ν˜„μž¬ λ„λ©”μΈμ—μ„œ λͺ» 찾으면 λΆ€λͺ¨ λ„λ©”μΈμ—μ„œ λ©”λͺ¨λ¦¬ 쑰회 - μ•ˆλœλ‹€. connection정보λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•˜λ―€λ‘œ μ‚¬μš©ν•  수 μ—†λ‹€.\n // if (domain.parentId) {\n // const parentCachedEntity = ConnectionManager.entities[domain.parentId]?.[name]\n // if (parentCachedEntity) {\n // return parentCachedEntity\n // }\n // }\n\n // 4. λΆ€λͺ¨ λ„λ©”μΈμ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회 (fallback)\n if (domain.parentId) {\n connection = await getRepository(Connection).findOne({\n where: {\n domain: { id: domain.parentId },\n name: name\n },\n relations: ['domain', 'edge', 'creator', 'updater']\n })\n\n if (connection) {\n const resolvedParams = await connection.getResolvedParameters()\n connection.params = resolvedParams\n connection.domain = domain\n connection.domainId = domain.id\n\n return connection\n }\n }\n\n return null\n } catch (error) {\n ConnectionManager.logger.error(`Failed to get connection entity '${name}' from database:`, error)\n return null\n }\n }\n\n static getConnectionInstanceEntityByName(domain: Domain, name: string): any {\n const connection = ConnectionManager.entities[domain.id]?.[name]\n if (connection) {\n return connection\n }\n\n if (domain.parentId) {\n return ConnectionManager.entities[domain.parentId]?.[name]\n }\n }\n\n /**\n * Creates a connection on-demand and returns the instance.\n * @param connection - The connection entity to create\n * @returns The connection instance\n */\n static async createOnDemandConnection(connection: Connection): Promise<any> {\n try {\n // 컀λ„₯μ…˜ 생성\n await connection.connect()\n\n ConnectionManager.logger.info(`On-demand connection '${connection.name}' created successfully`)\n return ConnectionManager.getConnectionInstance(connection)\n } catch (error) {\n ConnectionManager.logger.error(`Failed to create on-demand connection '${connection.name}':`, error)\n throw error\n }\n }\n\n /**\n * Disconnects an on-demand connection.\n * @param connection - The connection entity to disconnect\n */\n static async disconnectOnDemandConnection(connection: Connection): Promise<void> {\n try {\n await connection.disconnect()\n // ConnectionManager.removeConnectionInstance(connection)\n ConnectionManager.logger.info(`On-demand connection '${connection.name}' disconnected successfully`)\n } catch (error) {\n ConnectionManager.logger.error(`Failed to disconnect on-demand connection '${connection.name}':`, error)\n }\n }\n\n static getConnectionInstances(domain: Domain): { [connectionName: string]: any } {\n const connections = ConnectionManager.connections[domain.id]\n const parentConnections = domain.parentId && ConnectionManager.connections[domain.parentId]\n\n return {\n ...parentConnections,\n ...connections\n }\n }\n\n static getConnectionInstanceEntities(domain: Domain): { [connectionName: string]: any } {\n const connections = ConnectionManager.entities[domain.id]\n\n return {\n ...connections\n }\n }\n\n static addConnectionInstance(connection: Connection, instance: any) {\n const { domain, name } = connection\n\n var connections = ConnectionManager.connections[domain.id]\n if (!connections) {\n connections = ConnectionManager.connections[domain.id] = {}\n }\n\n var entities = ConnectionManager.entities[domain.id]\n if (!entities) {\n entities = ConnectionManager.entities[domain.id] = {}\n }\n\n connections[name] = instance\n entities[name] = connection\n\n ConnectionManager.publishState(connection, ConnectionStatus.CONNECTED)\n debug('add-connection', domain.subdomain, name)\n }\n\n static removeConnectionInstance(connection: Connection): any {\n const { domain, name } = connection\n var connections = ConnectionManager.connections[domain.id]\n var entities = ConnectionManager.entities[domain.id]\n\n var instance = connections?.[name]\n\n if (!connections || !instance) {\n debug('remove-connection', `'${name}' connection not found in domain '${domain.subdomain}'`)\n return\n }\n\n delete connections[name]\n delete entities[name]\n\n ConnectionManager.publishState(connection, ConnectionStatus.DISCONNECTED)\n debug('remove-connection', `'${name}' connection is removed from domain '${domain.subdomain}'`)\n\n return instance\n }\n\n private static async publishState(connection: Connection, state) {\n const { domain, id, name, description, type, edge } = connection\n\n pubsub.publish('connection-state', {\n connectionState: {\n domain,\n id,\n name,\n description,\n type,\n edge,\n state,\n timestamp: new Date()\n }\n })\n }\n}\n"]}
1
+ {"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../../server/engine/connection-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\nimport { createLogger, format, transports } from 'winston'\n\nimport { Domain, getRepository, pubsub, PubSubLogTransport } from '@things-factory/shell'\n\nimport { Connection, ConnectionInheritanceMode, ConnectionStatus } from '../service'\nimport { getDomainIdsWithAncestors } from '../utils/domain-inheritance'\nimport { Connector } from './types'\nimport { ProxyConnector } from './connector/proxy-connector'\n\nconst { combine, splat, printf, errors } = format\nconst debug = require('debug')('things-factory:integration-base:connections')\n\nfunction getSystemTimeZone() {\n try {\n const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone\n if (!timeZone) {\n throw new Error('Unable to resolve timeZone')\n }\n return timeZone\n } catch (e) {\n console.warn('Failed to get system timeZone, falling back to UTC.', e)\n return 'UTC'\n }\n}\n\nconst SYSTEM_TZ = getSystemTimeZone()\nconst systemTimestamp = format((info, opts: { tz?: string }) => {\n if (opts.tz) info.timestamp = moment().tz(opts.tz).format()\n return info\n})\n\nexport class ConnectionManager {\n private static connectors: { [propName: string]: Connector } = {}\n private static connections: { [domainId: string]: { [name: string]: any } } = {}\n private static entities = {}\n /**\n * Session 직렬화 mutex β€” connection name λ‹¨μœ„ (도메인 무관 κΈ€λ‘œλ²Œ).\n * sessionExclusive connector 의 acquireSessionPage λ™μ‹œ 호좜이 μ™ΈλΆ€ μ„Έμ…˜μ„ μ„œλ‘œ\n * invalidate ν•˜μ§€ μ•Šλ„λ‘ νμž‰. release 호좜 μ‹œ λ‹€μŒ λŒ€κΈ°μžκ°€ μ§„ν–‰.\n */\n private static sessionLocks: Map<string, Promise<void>> = new Map()\n private static logFormat = printf(({ level, message, timestamp, stack }) => {\n return `${timestamp} ${level}: ${stack || message}`\n })\n\n public static logger = createLogger({\n format: combine(errors({ stack: true }), systemTimestamp({ tz: SYSTEM_TZ }), splat(), ConnectionManager.logFormat),\n transports: [\n new (transports as any).DailyRotateFile({\n filename: `logs/connections-%DATE%.log`,\n datePattern: 'YYYY-MM-DD-HH',\n zippedArchive: false,\n maxSize: '20m',\n maxFiles: '14d',\n level: 'info'\n }),\n new PubSubLogTransport({\n topic: 'connection-log'\n })\n ]\n })\n\n static async ready() {\n const CONNECTIONS = await Promise.all(\n (\n await getRepository(Connection).find({\n where: { active: true },\n relations: ['domain', 'edge', 'creator', 'updater']\n })\n ).map(async connection => {\n // πŸ” ν•΄κ²°λœ νŒŒλΌλ―Έν„° μ‚¬μš©\n const resolvedParams = await connection.getResolvedParameters()\n\n return {\n ...connection,\n params: resolvedParams\n }\n })\n )\n\n ConnectionManager.logger.info('Initializing ConnectionManager...')\n\n return await Promise.all(\n [...Object.keys(ConnectionManager.connectors), 'proxy-connector'].map(type => {\n const connector = type == 'proxy-connector' ? ProxyConnector.instance : ConnectionManager.getConnector(type)\n\n ConnectionManager.logger.info(`Connector '${type}' started to ready`)\n\n return connector\n .ready(\n CONNECTIONS.filter(connection => {\n if (type == 'proxy-connector') {\n return !!connection.edge\n } else {\n return !connection.edge && connection.type == type\n }\n }) as any\n )\n .catch(error => {\n ConnectionManager.logger.error(error)\n })\n .then(() => {\n ConnectionManager.logger.info(`All connector for '${type}' ready`)\n })\n })\n ).then(() => {\n ConnectionManager.logger.info('ConnectionManager initialization done:')\n Object.keys(ConnectionManager.connections).forEach(key => {\n var connections = ConnectionManager.connections[key]\n ConnectionManager.logger.info('For domain(%s) : %s', key, JSON.stringify(Object.keys(connections)))\n })\n })\n }\n\n static registerConnector(type: string, connector: Connector) {\n ConnectionManager.connectors[type] = connector\n }\n\n static getConnector(type: string): Connector {\n return ConnectionManager.connectors[type]\n }\n\n static getConnectors(): { [connectorName: string]: Connector } {\n return {\n ...ConnectionManager.connectors\n }\n }\n\n static unregisterConnector(type: string) {\n delete ConnectionManager.connectors[type]\n }\n\n static getConnections() {\n return ConnectionManager.connections\n }\n\n static getEntities() {\n return ConnectionManager.entities\n }\n\n static getConnectionInstance(connection: Connection): any {\n const { domain, name } = connection\n return ConnectionManager.connections[domain.id]?.[name]\n }\n\n /**\n * μ„Έμ…˜ μ‚¬μš© νŒ¨ν„΄ (RAII).\n *\n * connector κ°€ sessionExclusive=true 둜 μ„ μ–Έν•œ 경우 connection.name λ‹¨μœ„ mutex 둜\n * 직렬화. ν˜ΈμΆœμžλŠ” acquireSessionPage / releasePage 의 lifecycle 을 μ‹ κ²½ μ“Έ ν•„μš” 없이\n * `withSession(connection, async page => { ... })` ν˜•νƒœλ‘œ μ‚¬μš©ν•˜λ©΄ 됨.\n *\n * stateless connector (sessionExclusive=false) λŠ” mutex 없이 μ¦‰μ‹œ μ§„ν–‰.\n *\n * @param connection 도메인 instance (connection.type μ—μ„œ connector 쑰회)\n * @param fn page λ₯Ό λ°›μ•„ 데이터 μž‘μ—…ν•˜λŠ” async ν•¨μˆ˜\n */\n static async withSession<T>(connection: any, fn: (page: any) => Promise<T>): Promise<T> {\n const connector = ConnectionManager.getConnector(connection.type)\n const exclusive = !!connector?.sessionExclusive\n\n const run = async (): Promise<T> => {\n const page = await connection.acquireSessionPage()\n try {\n return await fn(page)\n } finally {\n // releasePage κ°€ page 인자λ₯Ό λ°›μŒ β€” λˆ„λ½ μ‹œ λ‚΄λΆ€μ˜ page.close() 와\n // browser ν’€ λ°˜ν™˜μ΄ μŠ€ν‚΅λ˜μ–΄ λΈŒλΌμš°μ €κ°€ leak. ν’€ 고갈 β†’ 정상 μ’…λ£Œ hang 으둜 이어짐.\n try {\n await connection.releasePage?.(page)\n } catch (e) {\n ConnectionManager.logger.warn(`releasePage failed for '${connection.name}':`, e as any)\n }\n }\n }\n\n if (!exclusive) return await run()\n\n // mutex: connection name λ‹¨μœ„λ‘œ 직렬화 (도메인 무관, 같은 μ™ΈλΆ€ 계정 쀑볡 둜그인 λ°©μ§€)\n const lockKey = connection.name\n const wasQueued = ConnectionManager.sessionLocks.has(lockKey)\n const prev = ConnectionManager.sessionLocks.get(lockKey) || Promise.resolve()\n let release!: () => void\n const next = new Promise<void>(r => (release = r))\n // chain: 이전 μž‘μ—…μ΄ λλ‚œ λ’€ next κ°€ 끝날 λ•ŒκΉŒμ§€ pending β€” λ‹€μŒ λŒ€κΈ°μžκ°€ 이 chain 으둜 await\n const chain = prev.then(() => next)\n ConnectionManager.sessionLocks.set(lockKey, chain)\n\n // 큐 λŒ€κΈ° 진단 β€” 운영 쀑 \"μ™œ μž‘μ—…μ΄ stall λ˜λŠ”κ°€\" μΆ”μ μš©\n const waitStartedAt = wasQueued ? Date.now() : 0\n if (wasQueued) {\n ConnectionManager.logger.info(\n `[withSession] '${lockKey}' queued behind existing lock β€” waiting (domain='${connection.domain?.subdomain || connection.domainId}')`\n )\n }\n await prev\n if (wasQueued) {\n const waited = Date.now() - waitStartedAt\n ConnectionManager.logger.info(\n `[withSession] '${lockKey}' lock acquired after ${waited}ms wait`\n )\n }\n\n try {\n return await run()\n } finally {\n release()\n // λ‚΄κ°€ latest λ©΄ (이 chain λ’€λ‘œ μƒˆλ‘œ λ“€μ–΄μ˜¨ λŒ€κΈ°μž μ—†μŒ) cleanup\n if (ConnectionManager.sessionLocks.get(lockKey) === chain) {\n ConnectionManager.sessionLocks.delete(lockKey)\n }\n }\n }\n\n static async getConnectionInstanceByName(domain: Domain, name: string) {\n const connections = ConnectionManager.connections[domain.id]\n const connection = connections?.[name]\n\n if (!connection) {\n // μžμ‹ 도메인에 instance κ°€ 없을 λ•Œμ˜ μ„Έ κ°€μ§€ μžλ™ 처리 λΆ„κΈ°:\n //\n // (a) entity.__sharedFrom 있음 β€” SHARE λͺ¨λ“œ inheritance\n // β†’ λΆ€λͺ¨ λ„λ©”μΈμ˜ instance λ₯Ό κ·ΈλŒ€λ‘œ lookup ν•΄μ„œ λ°˜ν™˜. μžμ‹ λ„λ©”μΈμ—λŠ” instance 등둝 μ•ˆ 함.\n // 같은 instance κ°€ λΆ€λͺ¨Β·μžμ‹ λͺ¨λ‘μ—κ²Œ μ„œλΉ„μŠ€ 됨.\n //\n // (b) entity.__inheritedFrom 있음 β€” ISOLATE λͺ¨λ“œ inheritance\n // β†’ μžμ‹ 도메인 ν‚€ μ•„λž˜ μƒˆ instance μžλ™ 생성. cookies/μ„Έμ…˜ 격리.\n //\n // (c) entity.onDemand=true β€” λͺ…μ‹œμ  lazy 생성 (μžμ‹ 자체 record 인 κ²½μš°μ—λ„ 적용)\n // β†’ μžμ‹ 도메인 ν‚€ μ•„λž˜ μƒˆ instance 생성.\n //\n // λ‚˜λ¨Έμ§€: μžμ‹ record 도 μ—†κ³  inherit 도 μ•ˆ λ˜λŠ” 경우 β†’ throw.\n try {\n const connectionEntity = await ConnectionManager.getConnectionEntityByName(domain, name)\n\n if (connectionEntity?.__sharedFrom) {\n // SHARE: λΆ€λͺ¨ instance 직접 μ‚¬μš©\n const parentInstance = ConnectionManager.connections[connectionEntity.__sharedFrom]?.[name]\n if (parentInstance) return parentInstance\n throw `SHARE mode: parent instance for '${name}' (domain id '${connectionEntity.__sharedFrom}') not registered yet β€” startup ready() may have failed for parent`\n }\n\n if (connectionEntity?.onDemand || connectionEntity?.__inheritedFrom) {\n const instance = await ConnectionManager.createOnDemandConnection(connectionEntity!)\n return instance\n } else if (connectionEntity) {\n // entity λŠ” μ°Ύμ•˜λŠ”λ° instance 생성 trigger (onDemand/inherit) μ—†μŒ β€” μš΄μ˜μžκ°€ record 자체 점검 ν•„μš”\n throw `Connection '${name}' record exists but instance is not registered and no auto-create trigger (onDemand=false, no inheritance marker). Check whether parent record's active=true and ready() succeeded at startup.`\n } else {\n // entity 자체 λͺ» 찾음 β€” 도메인 트리 어디에도 record μ—†μŒ\n throw `Connection '${name}' not found in domain '${domain.subdomain || domain.id}' or any ancestor β€” verify the Connection record exists at the running domain or any of its ancestors.`\n }\n } catch (error) {\n throw `The connection with the given name(${name}) cannot be found: ${error}`\n }\n }\n\n return connection\n }\n\n static async getConnectionEntityByName(domain: Domain, name: string): Promise<Connection | null> {\n // 1. ν˜„μž¬ λ„λ©”μΈμ—μ„œ λ©”λͺ¨λ¦¬ 쑰회 (μš°μ„ )\n const cachedEntity = ConnectionManager.entities[domain.id]?.[name]\n if (cachedEntity) {\n return cachedEntity\n }\n\n // 2. ν˜„μž¬ λ„λ©”μΈμ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회 (μš°μ„ )\n try {\n let connection = await getRepository(Connection).findOne({\n where: {\n domain: { id: domain.id },\n name: name\n },\n relations: ['domain', 'edge', 'creator', 'updater']\n })\n\n if (connection) {\n const resolvedParams = await connection.getResolvedParameters()\n connection.params = resolvedParams\n return connection\n }\n\n // 3. ν˜„μž¬ λ„λ©”μΈμ—μ„œ λͺ» 찾으면 λΆ€λͺ¨ λ„λ©”μΈμ—μ„œ λ©”λͺ¨λ¦¬ 쑰회 - μ•ˆλœλ‹€. connection정보λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•˜λ―€λ‘œ μ‚¬μš©ν•  수 μ—†λ‹€.\n // if (domain.parentId) {\n // const parentCachedEntity = ConnectionManager.entities[domain.parentId]?.[name]\n // if (parentCachedEntity) {\n // return parentCachedEntity\n // }\n // }\n\n // 4. ancestor 도메인 트리 fallback (closest-first walk)\n //\n // EnvVar inheritance 와 μΌκ΄€λ˜κ²Œ λͺ¨λ“  쑰상 도메인을 closest-first 둜 탐색.\n // 예: SYSTEM β†’ μ‹œκ³΅μ‚¬ β†’ ν”„λ‘œμ νŠΈ νŠΈλ¦¬μ—μ„œ μ‹œλ‚˜λ¦¬μ˜€κ°€ ν”„λ‘œμ νŠΈ λ„λ©”μΈμ—μ„œ 싀행될 λ•Œ\n // Connection record κ°€ SYSTEM λ˜λŠ” μ‹œκ³΅μ‚¬ 어디에 μžˆμ–΄λ„ 찾아냄.\n //\n // 맀칭된 ancestor 의 inheritanceMode 에 따라 두 갈래:\n // ISOLATE (default): μžμ‹ λ„λ©”μΈμš© clone 생성. cookies/μ„Έμ…˜ 격리, μžμ‹ μ»¨ν…μŠ€νŠΈμ—μ„œ\n // EnvVar override ν•΄μ†Œ. __inheritedFrom marker 둜 후속 μžλ™ instance.\n // SHARE: λΆ€λͺ¨ entity κ·ΈλŒ€λ‘œ λ°˜ν™˜. paramsΒ·μ„Έμ…˜Β·μΈμŠ€ν„΄μŠ€ λͺ¨λ‘ λΆ€λͺ¨ 것 곡유.\n // __sharedFrom marker 둜 instance lookup 이 λΆ€λͺ¨ 도메인을 κ°€λ¦¬ν‚€κ²Œ 함.\n //\n // ν˜Έν™˜μ„±: inheritanceMode λ―Έμ§€μ •(NULL) β†’ connector default β†’ 폴백 ISOLATE.\n // κΈ°μ‘΄ λͺ¨λ“  Connection 은 이 column μΆ”κ°€ μ „ λ§Œλ“€μ–΄μ‘ŒμœΌλ―€λ‘œ NULL β†’ ISOLATE = κΈ°μ‘΄ 거동.\n const ancestorIds = await getDomainIdsWithAncestors(domain)\n // ancestorIds[0] = domain.id (이미 μœ„ λΆ„κΈ°μ—μ„œ μ‹œλ„), skip\n const searchedAncestors: string[] = []\n for (let i = 1; i < ancestorIds.length; i++) {\n const ancestorId = ancestorIds[i]\n searchedAncestors.push(ancestorId)\n const parentConn = await getRepository(Connection).findOne({\n where: {\n domain: { id: ancestorId },\n name: name\n },\n relations: ['domain', 'edge', 'creator', 'updater']\n })\n\n if (!parentConn) continue // 이 ancestor 에 μ—†μœΌλ©΄ λ‹€μŒ ancestor\n\n ConnectionManager.logger.info(\n `[inheritance] '${name}' inherited by domain '${domain.subdomain || domain.id}' ` +\n `from ancestor '${parentConn.domain?.subdomain || ancestorId}' ` +\n `(walked ${i} ancestor(s))`\n )\n\n const connectorDefault = ConnectionManager.connectors[parentConn.type]?.inheritanceMode\n const effectiveMode =\n parentConn.inheritanceMode || connectorDefault || ConnectionInheritanceMode.ISOLATE\n\n if (effectiveMode === ConnectionInheritanceMode.SHARE) {\n // SHARE β€” λΆ€λͺ¨ entity κ·ΈλŒ€λ‘œ λ°˜ν™˜ (mutation μ΅œμ†Œν™”).\n const shared: any = parentConn\n shared.__sharedFrom = parentConn.domainId\n shared.params = await parentConn.getResolvedParameters()\n return shared\n }\n\n // ISOLATE (default) β€” clone + marker\n const inherited: any = Object.assign(\n Object.create(Object.getPrototypeOf(parentConn)),\n parentConn,\n {\n domain,\n domainId: domain.id,\n cookies: undefined,\n __inheritedFrom: parentConn.domainId\n }\n )\n inherited.params = await inherited.getResolvedParameters()\n return inherited\n }\n\n // μ–΄λŠ ancestor μ—μ„œλ„ λͺ» 찾음 β€” μš΄μ˜μžκ°€ μ–΄λ””κΉŒμ§€ μ°Ύμ•˜λŠ”μ§€ μ•Œ 수 μžˆλ„λ‘ 진단 둜그\n if (searchedAncestors.length > 0) {\n ConnectionManager.logger.warn(\n `[inheritance] '${name}' not found at any ancestor of domain '${domain.subdomain || domain.id}' ` +\n `β€” searched: [${searchedAncestors.join(', ')}]`\n )\n }\n return null\n } catch (error) {\n ConnectionManager.logger.error(`Failed to get connection entity '${name}' from database:`, error)\n return null\n }\n }\n\n static getConnectionInstanceEntityByName(domain: Domain, name: string): any {\n const connection = ConnectionManager.entities[domain.id]?.[name]\n if (connection) {\n return connection\n }\n\n if (domain.parentId) {\n return ConnectionManager.entities[domain.parentId]?.[name]\n }\n }\n\n /**\n * Creates a connection on-demand and returns the instance.\n * @param connection - The connection entity to create\n * @returns The connection instance\n */\n static async createOnDemandConnection(connection: Connection): Promise<any> {\n try {\n // 컀λ„₯μ…˜ 생성\n await connection.connect()\n\n ConnectionManager.logger.info(`On-demand connection '${connection.name}' created successfully`)\n return ConnectionManager.getConnectionInstance(connection)\n } catch (error) {\n ConnectionManager.logger.error(`Failed to create on-demand connection '${connection.name}':`, error)\n throw error\n }\n }\n\n /**\n * Disconnects an on-demand connection.\n * @param connection - The connection entity to disconnect\n */\n static async disconnectOnDemandConnection(connection: Connection): Promise<void> {\n try {\n await connection.disconnect()\n // ConnectionManager.removeConnectionInstance(connection)\n ConnectionManager.logger.info(`On-demand connection '${connection.name}' disconnected successfully`)\n } catch (error) {\n ConnectionManager.logger.error(`Failed to disconnect on-demand connection '${connection.name}':`, error)\n }\n }\n\n static getConnectionInstances(domain: Domain): { [connectionName: string]: any } {\n const connections = ConnectionManager.connections[domain.id]\n const parentConnections = domain.parentId && ConnectionManager.connections[domain.parentId]\n\n return {\n ...parentConnections,\n ...connections\n }\n }\n\n static getConnectionInstanceEntities(domain: Domain): { [connectionName: string]: any } {\n const connections = ConnectionManager.entities[domain.id]\n\n return {\n ...connections\n }\n }\n\n static addConnectionInstance(connection: Connection, instance: any) {\n const { domain, name } = connection\n\n var connections = ConnectionManager.connections[domain.id]\n if (!connections) {\n connections = ConnectionManager.connections[domain.id] = {}\n }\n\n var entities = ConnectionManager.entities[domain.id]\n if (!entities) {\n entities = ConnectionManager.entities[domain.id] = {}\n }\n\n connections[name] = instance\n entities[name] = connection\n\n ConnectionManager.publishState(connection, ConnectionStatus.CONNECTED)\n debug('add-connection', domain.subdomain, name)\n }\n\n static removeConnectionInstance(connection: Connection): any {\n const { domain, name } = connection\n var connections = ConnectionManager.connections[domain.id]\n var entities = ConnectionManager.entities[domain.id]\n\n var instance = connections?.[name]\n\n if (!connections || !instance) {\n debug('remove-connection', `'${name}' connection not found in domain '${domain.subdomain}'`)\n return\n }\n\n delete connections[name]\n delete entities[name]\n\n ConnectionManager.publishState(connection, ConnectionStatus.DISCONNECTED)\n debug('remove-connection', `'${name}' connection is removed from domain '${domain.subdomain}'`)\n\n return instance\n }\n\n private static async publishState(connection: Connection, state) {\n const { domain, id, name, description, type, edge } = connection\n\n pubsub.publish('connection-state', {\n connectionState: {\n domain,\n id,\n name,\n description,\n type,\n edge,\n state,\n timestamp: new Date()\n }\n })\n }\n}\n"]}
@@ -1,5 +1,17 @@
1
1
  import { Connector } from '../types';
2
2
  export declare class HeadlessConnector implements Connector {
3
+ /**
4
+ * μ™ΈλΆ€ μ‚¬μ΄νŠΈ 둜그인 μ„Έμ…˜ 보유 β€” 같은 connection name 의 λ™μ‹œ acquireSessionPage
5
+ * 호좜이 같은 μ™ΈλΆ€ 계정 μ„Έμ…˜μ„ μ„œλ‘œ invalidate ν•˜μ§€ μ•Šλ„λ‘ ConnectionManager κ°€
6
+ * mutex 직렬화. 상속 connector (allbaro, kiscon λ“±) μžλ™ 적용.
7
+ */
8
+ sessionExclusive: boolean;
9
+ /**
10
+ * μžμ‹ 도메인이 λΆ€λͺ¨μ˜ Connection μ •μ˜λ₯Ό inherit ν•  λ•Œ μžμ‹λ³„ μƒˆ μΈμŠ€ν„΄μŠ€ 생성.
11
+ * cookies/μ„Έμ…˜Β·μžκ²©μ¦λͺ… 격리. ν—€λ“œλ¦¬μŠ€ μ‚¬μ΄νŠΈ connector λŠ” μ™ΈλΆ€ 자격증λͺ…이
12
+ * μžμ‹ (ν…Œλ„ŒνŠΈ) λ§ˆλ‹€ λ‹€λ₯Ό 수 μžˆμœΌλ―€λ‘œ ISOLATE κ°€ μžμ—° default.
13
+ */
14
+ inheritanceMode: "isolate";
3
15
  ready(connectionConfigs: any): Promise<void>;
4
16
  connect(connection: any): Promise<void>;
5
17
  setupPage(page: any, uri: any, timeout: any): Promise<void>;