@things-factory/board-service 10.0.0-beta.91 → 10.0.0-beta.93

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.
@@ -30,15 +30,15 @@ async function setupLabelPage(browser) {
30
30
  }
31
31
  });
32
32
  page.on('pageerror', error => {
33
- console.log(`[headless pageerror] ${error.message}`);
34
- console.log(error.stack);
33
+ console.error(`[headless pageerror] ${error.message}`);
34
+ console.error(error.stack);
35
35
  });
36
36
  page.on('error', error => {
37
- console.log(`[headless fault] ${error}`);
38
- console.log(error.stack);
37
+ console.error(`[headless fault] ${error}`);
38
+ console.error(error.stack);
39
39
  });
40
40
  page.on('requestfailed', request => {
41
- console.log('Request failed:', request.url());
41
+ console.error('Request failed:', request.url());
42
42
  });
43
43
  page.on('request', request => {
44
44
  if (request.url() === url) {
@@ -1 +1 @@
1
- {"version":3,"file":"headless-pool-for-label.js","sourceRoot":"","sources":["../../server/controllers/headless-pool-for-label.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAwGH,0CAYC;AAlHD,iDAA4F;AAC5F,iDAA8C;AAC9C,yCAAkC;AAElC,uCAAuC;AACvC,KAAK,UAAU,cAAc,CAAC,OAAY;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAA;IACvB,MAAM,IAAI,GAAG,WAAW,CAAA;IACxB,MAAM,IAAI,GAAG,8BAA8B,CAAA;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA;IAC7B,MAAM,GAAG,GAAG,GAAG,QAAQ,MAAM,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,CAAA;IAElD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IACpC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG,MAAM,IAAA,gBAAK,GAAE,CAAA;IAE9C,MAAM,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAA;IAEvC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE;QAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;QAE7F,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAA;QAC/E,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;YACvB,IAAI,IAAI;gBAAE,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QACzD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE;QAC3B,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;QACvB,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,EAAE,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE;QACjC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE;QAC3B,IAAI,OAAO,CAAC,GAAG,EAAE,KAAK,GAAG,EAAE,CAAC;YAC1B,OAAO,CAAC,QAAQ,CAAC;gBACf,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC;oBACvB,KAAK,EAAE,UAAU;oBACjB,UAAU;iBACX,CAAC;aACH,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,QAAQ,EAAE,CAAA;QACpB,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;IAEvD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,EAAE,EAAE,CAAC,CAAA;AAC/E,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,IAAI,EAAE,EAAE,CAAC,CAAA;AAEhF,mDAAmD;AACnD,MAAM,SAAS,GAAG,IAAA,+BAAuB,EAAC,OAAO,EAAE;IACjD,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,cAAc;IACnB,IAAI,EAAE;QACJ,GAAG,mCAA2B,CAAC,KAAK;QACpC,GAAG,mCAA2B,CAAC,aAAa;KAC7C;IACD,oBAAoB,EAAE,KAAK;IAC3B,YAAY,EAAE,IAAI;IAClB,aAAa,EAAE,IAAI;IACnB,WAAW,EAAE,cAAc;CAC5B,CAAC,CAAA;AAEF,iCAAiC;AACjC,UAAU,CAAC,KAAK,IAAI,EAAE;IACpB,MAAM,WAAW,GAAG,cAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAA;IAC3D,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QACrC,mCAAmC;QACnC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAA;IACzB,CAAC;AACH,CAAC,CAAC,CAAA;AAEF;;;GAGG;AACH,SAAgB,eAAe;IAC7B,OAAO;QACL,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE;QAClC,OAAO,EAAE,CAAC,QAAa,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;QACvD,IAAI,EAAE,CAAC,EAAE,iDAAiD;QAC1D,SAAS,EAAE,CAAC;QACZ,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,CAAC;QACV,GAAG,EAAE,cAAc;QACnB,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,gCAAgC;KAChE,CAAA;AACH,CAAC","sourcesContent":["/**\n * Label Service Headless Pool\n * Using the unified headless pool system from @things-factory/shell\n */\n\nimport { getOrCreateHeadlessPool, HEADLESS_POOL_ARGUMENT_SETS } from '@things-factory/shell'\nimport { pubsub } from '@things-factory/shell'\nimport { fonts } from './fonts.js'\n\n// Custom setup function for label page\nasync function setupLabelPage(browser: any) {\n const protocol = 'http'\n const host = 'localhost'\n const path = '/internal-label-command-view'\n const port = process.env.PORT\n const url = `${protocol}://${host}:${port}${path}`\n\n const page = await browser.newPage()\n const [fontsToUse, fontStyles] = await fonts()\n\n await page.setRequestInterception(true)\n\n page.on('console', async msg => {\n const args = await Promise.all(msg.args().map(arg => arg.jsonValue().catch(() => undefined)))\n\n if (args.some(a => a !== undefined)) {\n console.log(`[headless ${msg.type()}]`, ...args.filter(a => a !== undefined))\n } else {\n const text = msg.text()\n if (text) console.log(`[headless ${msg.type()}]`, text)\n }\n })\n\n page.on('pageerror', error => {\n console.log(`[headless pageerror] ${error.message}`)\n console.log(error.stack)\n })\n\n page.on('error', error => {\n console.log(`[headless fault] ${error}`)\n console.log(error.stack)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n\n page.on('request', request => {\n if (request.url() === url) {\n request.continue({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n postData: JSON.stringify({\n fonts: fontsToUse,\n fontStyles\n })\n })\n } else {\n request.continue()\n }\n })\n\n await page.goto(url, { timeout: 0, waitUntil: 'load' })\n\n return { browser, page }\n}\n\n/**\n * 라벨 출력은 (자동 출고 워크플로우 등) 즉시성 요구 — cold start 비용 (browser+page\n * setup ~2-3 초) 이 사용자 경험에 직결. min=1 로 최소 1 개 warm 유지가 기본.\n * 사용 빈도가 매우 낮은 사이트는 환경 변수로 0 까지 낮출 수 있다.\n *\n * HEADLESS_POOL_LABEL_MIN (default 1)\n * HEADLESS_POOL_LABEL_MAX (default 10)\n */\nconst LABEL_POOL_MIN = parseInt(process.env.HEADLESS_POOL_LABEL_MIN ?? '1', 10)\nconst LABEL_POOL_MAX = parseInt(process.env.HEADLESS_POOL_LABEL_MAX ?? '10', 10)\n\n// Create the label pool instance with custom setup\nconst labelPool = getOrCreateHeadlessPool('label', {\n min: LABEL_POOL_MIN,\n max: LABEL_POOL_MAX,\n args: [\n ...HEADLESS_POOL_ARGUMENT_SETS.basic,\n ...HEADLESS_POOL_ARGUMENT_SETS.keychain_safe\n ],\n acquireTimeoutMillis: 15000,\n testOnBorrow: true,\n enableCleanup: true,\n customSetup: setupLabelPage\n})\n\n// Font change event subscription\nsetTimeout(async () => {\n const eventSource = pubsub.subscribe('notify-font-changed')\n for await (const data of eventSource) {\n // Reset the pool when fonts change\n await labelPool.reset()\n }\n})\n\n/**\n * Get the label headless pool\n * @returns Pool instance with acquire/release methods\n */\nexport function getHeadlessPool() {\n return {\n acquire: () => labelPool.acquire(),\n release: (resource: any) => labelPool.release(resource),\n size: 0, // These will be dynamically calculated if needed\n available: 0,\n borrowed: 0,\n pending: 0,\n max: LABEL_POOL_MAX,\n min: LABEL_POOL_MIN,\n clear: () => labelPool.reset() // For font change compatibility\n }\n}"]}
1
+ {"version":3,"file":"headless-pool-for-label.js","sourceRoot":"","sources":["../../server/controllers/headless-pool-for-label.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Label Service Headless Pool\n * Using the unified headless pool system from @things-factory/shell\n */\n\nimport { getOrCreateHeadlessPool, HEADLESS_POOL_ARGUMENT_SETS } from '@things-factory/shell'\nimport { pubsub } from '@things-factory/shell'\nimport { fonts } from './fonts.js'\n\n// Custom setup function for label page\nasync function setupLabelPage(browser: any) {\n const protocol = 'http'\n const host = 'localhost'\n const path = '/internal-label-command-view'\n const port = process.env.PORT\n const url = `${protocol}://${host}:${port}${path}`\n\n const page = await browser.newPage()\n const [fontsToUse, fontStyles] = await fonts()\n\n await page.setRequestInterception(true)\n\n page.on('console', async msg => {\n const args = await Promise.all(msg.args().map(arg => arg.jsonValue().catch(() => undefined)))\n\n if (args.some(a => a !== undefined)) {\n console.log(`[headless ${msg.type()}]`, ...args.filter(a => a !== undefined))\n } else {\n const text = msg.text()\n if (text) console.log(`[headless ${msg.type()}]`, text)\n }\n })\n\n page.on('pageerror', error => {\n console.error(`[headless pageerror] ${error.message}`)\n console.error(error.stack)\n })\n\n page.on('error', error => {\n console.error(`[headless fault] ${error}`)\n console.error(error.stack)\n })\n\n page.on('requestfailed', request => {\n console.error('Request failed:', request.url())\n })\n\n page.on('request', request => {\n if (request.url() === url) {\n request.continue({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n postData: JSON.stringify({\n fonts: fontsToUse,\n fontStyles\n })\n })\n } else {\n request.continue()\n }\n })\n\n await page.goto(url, { timeout: 0, waitUntil: 'load' })\n\n return { browser, page }\n}\n\n/**\n * 라벨 출력은 (자동 출고 워크플로우 등) 즉시성 요구 — cold start 비용 (browser+page\n * setup ~2-3 초) 이 사용자 경험에 직결. min=1 로 최소 1 개 warm 유지가 기본.\n * 사용 빈도가 매우 낮은 사이트는 환경 변수로 0 까지 낮출 수 있다.\n *\n * HEADLESS_POOL_LABEL_MIN (default 1)\n * HEADLESS_POOL_LABEL_MAX (default 10)\n */\nconst LABEL_POOL_MIN = parseInt(process.env.HEADLESS_POOL_LABEL_MIN ?? '1', 10)\nconst LABEL_POOL_MAX = parseInt(process.env.HEADLESS_POOL_LABEL_MAX ?? '10', 10)\n\n// Create the label pool instance with custom setup\nconst labelPool = getOrCreateHeadlessPool('label', {\n min: LABEL_POOL_MIN,\n max: LABEL_POOL_MAX,\n args: [\n ...HEADLESS_POOL_ARGUMENT_SETS.basic,\n ...HEADLESS_POOL_ARGUMENT_SETS.keychain_safe\n ],\n acquireTimeoutMillis: 15000,\n testOnBorrow: true,\n enableCleanup: true,\n customSetup: setupLabelPage\n})\n\n// Font change event subscription\nsetTimeout(async () => {\n const eventSource = pubsub.subscribe('notify-font-changed')\n for await (const data of eventSource) {\n // Reset the pool when fonts change\n await labelPool.reset()\n }\n})\n\n/**\n * Get the label headless pool\n * @returns Pool instance with acquire/release methods\n */\nexport function getHeadlessPool() {\n return {\n acquire: () => labelPool.acquire(),\n release: (resource: any) => labelPool.release(resource),\n size: 0, // These will be dynamically calculated if needed\n available: 0,\n borrowed: 0,\n pending: 0,\n max: LABEL_POOL_MAX,\n min: LABEL_POOL_MIN,\n clear: () => labelPool.reset() // For font change compatibility\n }\n}"]}
@@ -29,7 +29,7 @@ function attachPageListeners(page) {
29
29
  console.error(error.stack);
30
30
  });
31
31
  page.on('requestfailed', request => {
32
- console.log('Request failed:', request.url());
32
+ console.error('Request failed:', request.url());
33
33
  });
34
34
  }
35
35
  /**
@@ -111,7 +111,7 @@ async function withHeadlessPage({ id = '', model = null, data = null, width: w =
111
111
  result = await renderFn(page);
112
112
  }
113
113
  catch (error) {
114
- console.log(error);
114
+ console.error(error);
115
115
  }
116
116
  finally {
117
117
  await page.close();
@@ -1 +1 @@
1
- {"version":3,"file":"headless-render.js","sourceRoot":"","sources":["../../server/controllers/headless-render.ts"],"names":[],"mappings":"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","sourcesContent":["import { fonts } from './fonts.js'\nimport { getHeadlessPool } from './headless-pool-for-board.js'\nimport { headlessModel } from './headless-model.js'\n\nconst protocol = 'http'\nconst host = 'localhost'\nconst path = '/internal-board-service-view'\n\n/** headless page 에 console/error 이벤트 핸들러를 일괄 등록한다. */\nfunction attachPageListeners(page: any) {\n page.on('console', async msg => {\n const args = await Promise.all(msg.args().map(arg => arg.jsonValue().catch(() => undefined)))\n\n if (args.some(a => a !== undefined)) {\n console.log(`[headless ${msg.type()}]`, ...args.filter(a => a !== undefined))\n } else {\n const text = msg.text()\n if (text) console.log(`[headless ${msg.type()}]`, text)\n }\n })\n\n page.on('pageerror', error => {\n console.error(`[headless pageerror] ${error.message}`)\n console.error(error.stack)\n })\n\n page.on('error', error => {\n console.error(`[headless fault] ${error}`)\n console.error(error.stack)\n })\n\n page.on('requestfailed', request => {\n console.log('Request failed:', request.url())\n })\n}\n\nexport interface HeadlessRenderOptions {\n id?: string\n model?: any\n data?: any\n width?: number\n height?: number\n options?: any\n context?: any\n draft?: boolean\n /** true 이면 thumbnail 크기(400x300)로 축소 */\n isThumbnail?: boolean\n}\n\n/**\n * headless 브라우저 페이지를 준비하고 renderFn 을 실행한 결과를 반환한다.\n *\n * 공통 처리: pool acquire → headlessModel → fonts → viewport → request intercept → goto → data 주입 → renderFn → cleanup\n */\nexport async function withHeadlessPage<T>(\n {\n id = '',\n model = null,\n data = null,\n width: w = 0,\n height: h = 0,\n options = {} as any,\n context = {} as any,\n draft = false,\n isThumbnail = false\n }: HeadlessRenderOptions,\n renderFn: (page: any) => Promise<T>\n): Promise<T | null> {\n const browser = (await getHeadlessPool().acquire()) as any\n if (!browser) return null\n\n const { domain, user } = context.state\n\n var { model: resolvedModel, base } = await headlessModel({ domain, id, model }, draft)\n const [fontsToUse, fontStyles] = await fonts(domain)\n\n resolvedModel.fonts = fontsToUse\n resolvedModel.fontStyles = fontStyles\n\n let { width, height } = resolvedModel\n\n if (isThumbnail) {\n const ratio = Math.min(400 / width, 300 / height)\n width = width * ratio\n height = height * ratio\n } else if (w || h) {\n const ratio = Math.min((w || width) / width, (h || height) / height)\n width = width * ratio\n height = height * ratio\n }\n\n width = Math.floor(w || Number(width))\n height = Math.floor(h || Number(height))\n\n const port = process.env.PORT ? `:${process.env.PORT}` : ''\n const url = `${protocol}://${host}${port}${path}`\n const page = await browser.newPage()\n\n let result: T | null = null\n\n try {\n await page.setViewport({ width, height })\n await page.setRequestInterception(true)\n await page.setDefaultTimeout(10000)\n\n attachPageListeners(page)\n\n const token = await user?.sign()\n\n page.on('request', request => {\n if (request.url() === url) {\n request.continue({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-things-factory-domain': domain?.subdomain,\n Authorization: 'Bearer ' + token\n },\n postData: JSON.stringify({ model: resolvedModel, base })\n })\n } else if (request.url().startsWith(`${protocol}://${host}${port}`)) {\n request.continue({\n headers: {\n ...request.headers(),\n 'x-things-factory-domain': domain?.subdomain,\n Authorization: 'Bearer ' + token\n }\n })\n } else {\n request.continue()\n }\n })\n\n await page.goto(url)\n\n await page.evaluate(async data => {\n if (data) {\n // @ts-ignore\n s.data = data\n }\n return new Promise(resolve => {\n // @ts-ignore\n requestAnimationFrame(() => resolve())\n })\n }, data)\n\n // board 내부 async 로딩 (차트 데이터, 이미지, GraphQL query 등) 정착까지\n // 대기. timeout 시 무시 (= 빠른 정적 board 영향 최소화).\n await page.waitForNetworkIdle().catch(() => {})\n\n result = await renderFn(page)\n } catch (error) {\n console.log(error)\n } finally {\n await page.close()\n getHeadlessPool().release(browser)\n }\n\n return result\n}\n"]}
1
+ {"version":3,"file":"headless-render.js","sourceRoot":"","sources":["../../server/controllers/headless-render.ts"],"names":[],"mappings":"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","sourcesContent":["import { fonts } from './fonts.js'\nimport { getHeadlessPool } from './headless-pool-for-board.js'\nimport { headlessModel } from './headless-model.js'\n\nconst protocol = 'http'\nconst host = 'localhost'\nconst path = '/internal-board-service-view'\n\n/** headless page 에 console/error 이벤트 핸들러를 일괄 등록한다. */\nfunction attachPageListeners(page: any) {\n page.on('console', async msg => {\n const args = await Promise.all(msg.args().map(arg => arg.jsonValue().catch(() => undefined)))\n\n if (args.some(a => a !== undefined)) {\n console.log(`[headless ${msg.type()}]`, ...args.filter(a => a !== undefined))\n } else {\n const text = msg.text()\n if (text) console.log(`[headless ${msg.type()}]`, text)\n }\n })\n\n page.on('pageerror', error => {\n console.error(`[headless pageerror] ${error.message}`)\n console.error(error.stack)\n })\n\n page.on('error', error => {\n console.error(`[headless fault] ${error}`)\n console.error(error.stack)\n })\n\n page.on('requestfailed', request => {\n console.error('Request failed:', request.url())\n })\n}\n\nexport interface HeadlessRenderOptions {\n id?: string\n model?: any\n data?: any\n width?: number\n height?: number\n options?: any\n context?: any\n draft?: boolean\n /** true 이면 thumbnail 크기(400x300)로 축소 */\n isThumbnail?: boolean\n}\n\n/**\n * headless 브라우저 페이지를 준비하고 renderFn 을 실행한 결과를 반환한다.\n *\n * 공통 처리: pool acquire → headlessModel → fonts → viewport → request intercept → goto → data 주입 → renderFn → cleanup\n */\nexport async function withHeadlessPage<T>(\n {\n id = '',\n model = null,\n data = null,\n width: w = 0,\n height: h = 0,\n options = {} as any,\n context = {} as any,\n draft = false,\n isThumbnail = false\n }: HeadlessRenderOptions,\n renderFn: (page: any) => Promise<T>\n): Promise<T | null> {\n const browser = (await getHeadlessPool().acquire()) as any\n if (!browser) return null\n\n const { domain, user } = context.state\n\n var { model: resolvedModel, base } = await headlessModel({ domain, id, model }, draft)\n const [fontsToUse, fontStyles] = await fonts(domain)\n\n resolvedModel.fonts = fontsToUse\n resolvedModel.fontStyles = fontStyles\n\n let { width, height } = resolvedModel\n\n if (isThumbnail) {\n const ratio = Math.min(400 / width, 300 / height)\n width = width * ratio\n height = height * ratio\n } else if (w || h) {\n const ratio = Math.min((w || width) / width, (h || height) / height)\n width = width * ratio\n height = height * ratio\n }\n\n width = Math.floor(w || Number(width))\n height = Math.floor(h || Number(height))\n\n const port = process.env.PORT ? `:${process.env.PORT}` : ''\n const url = `${protocol}://${host}${port}${path}`\n const page = await browser.newPage()\n\n let result: T | null = null\n\n try {\n await page.setViewport({ width, height })\n await page.setRequestInterception(true)\n await page.setDefaultTimeout(10000)\n\n attachPageListeners(page)\n\n const token = await user?.sign()\n\n page.on('request', request => {\n if (request.url() === url) {\n request.continue({\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-things-factory-domain': domain?.subdomain,\n Authorization: 'Bearer ' + token\n },\n postData: JSON.stringify({ model: resolvedModel, base })\n })\n } else if (request.url().startsWith(`${protocol}://${host}${port}`)) {\n request.continue({\n headers: {\n ...request.headers(),\n 'x-things-factory-domain': domain?.subdomain,\n Authorization: 'Bearer ' + token\n }\n })\n } else {\n request.continue()\n }\n })\n\n await page.goto(url)\n\n await page.evaluate(async data => {\n if (data) {\n // @ts-ignore\n s.data = data\n }\n return new Promise(resolve => {\n // @ts-ignore\n requestAnimationFrame(() => resolve())\n })\n }, data)\n\n // board 내부 async 로딩 (차트 데이터, 이미지, GraphQL query 등) 정착까지\n // 대기. timeout 시 무시 (= 빠른 정적 board 영향 최소화).\n await page.waitForNetworkIdle().catch(() => {})\n\n result = await renderFn(page)\n } catch (error) {\n console.error(error)\n } finally {\n await page.close()\n getHeadlessPool().release(browser)\n }\n\n return result\n}\n"]}