@things-factory/board-service 10.1.39 → 10.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/controllers/headless-pool-for-label.js +33 -19
- package/dist-server/controllers/headless-pool-for-label.js.map +1 -1
- package/dist-server/controllers/headless-pool-for-label.test.d.ts +10 -0
- package/dist-server/controllers/headless-pool-for-label.test.js +52 -0
- package/dist-server/controllers/headless-pool-for-label.test.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.getHeadlessPool = getHeadlessPool;
|
|
8
8
|
const shell_1 = require("@things-factory/shell");
|
|
9
|
-
const shell_2 = require("@things-factory/shell");
|
|
10
9
|
const fonts_js_1 = require("./fonts.js");
|
|
11
10
|
// Custom setup function for label page
|
|
12
11
|
async function setupLabelPage(browser) {
|
|
@@ -70,25 +69,40 @@ async function setupLabelPage(browser) {
|
|
|
70
69
|
*/
|
|
71
70
|
const LABEL_POOL_MIN = parseInt(process.env.HEADLESS_POOL_LABEL_MIN ?? '1', 10);
|
|
72
71
|
const LABEL_POOL_MAX = parseInt(process.env.HEADLESS_POOL_LABEL_MAX ?? '10', 10);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
/*
|
|
73
|
+
* The pool is made when the server has started, not when this module is loaded.
|
|
74
|
+
*
|
|
75
|
+
* With min=1 the pool builds a browser as soon as it exists, and the page setup reads fonts from the database and
|
|
76
|
+
* loads a page from this same server. At module load neither is ready: shell's databaseInitializer is still running
|
|
77
|
+
* synchronize(), and on sqlite every query shares the one query runner. The fonts query's release() cleared the
|
|
78
|
+
* runner's loaded-table list mid-synchronize, which then created env_vars again and died on 'already exists' —
|
|
79
|
+
* plant did not start after 10.1.38 (seen 2026-09-24; it showed then because ops-master made synchronize rebuild
|
|
80
|
+
* five tables, a longer window). 'bootstrap-module-start' comes after the database and after the server listens.
|
|
81
|
+
*
|
|
82
|
+
* A caller that needs a label before that — none should, since routes are served only after it — still gets a pool.
|
|
83
|
+
*/
|
|
84
|
+
let labelPool;
|
|
85
|
+
function pool() {
|
|
86
|
+
labelPool ??= (0, shell_1.getOrCreateHeadlessPool)('label', {
|
|
87
|
+
min: LABEL_POOL_MIN,
|
|
88
|
+
max: LABEL_POOL_MAX,
|
|
89
|
+
args: [...shell_1.HEADLESS_POOL_ARGUMENT_SETS.basic, ...shell_1.HEADLESS_POOL_ARGUMENT_SETS.keychain_safe],
|
|
90
|
+
acquireTimeoutMillis: 15000,
|
|
91
|
+
testOnBorrow: true,
|
|
92
|
+
enableCleanup: true,
|
|
93
|
+
customSetup: setupLabelPage
|
|
94
|
+
});
|
|
95
|
+
return labelPool;
|
|
96
|
+
}
|
|
97
|
+
(0, shell_1.onHook)('bootstrap-module-start', () => {
|
|
98
|
+
pool();
|
|
85
99
|
});
|
|
86
100
|
// Font change event subscription
|
|
87
101
|
setTimeout(async () => {
|
|
88
|
-
const eventSource =
|
|
102
|
+
const eventSource = shell_1.pubsub.subscribe('notify-font-changed');
|
|
89
103
|
for await (const data of eventSource) {
|
|
90
|
-
// Reset the pool when fonts change
|
|
91
|
-
await labelPool
|
|
104
|
+
// Reset the pool when fonts change; a pool not made yet will read the new fonts when it is.
|
|
105
|
+
await labelPool?.reset();
|
|
92
106
|
}
|
|
93
107
|
});
|
|
94
108
|
/**
|
|
@@ -97,15 +111,15 @@ setTimeout(async () => {
|
|
|
97
111
|
*/
|
|
98
112
|
function getHeadlessPool() {
|
|
99
113
|
return {
|
|
100
|
-
acquire: () =>
|
|
101
|
-
release: (resource) =>
|
|
114
|
+
acquire: () => pool().acquire(),
|
|
115
|
+
release: (resource) => pool().release(resource),
|
|
102
116
|
size: 0, // These will be dynamically calculated if needed
|
|
103
117
|
available: 0,
|
|
104
118
|
borrowed: 0,
|
|
105
119
|
pending: 0,
|
|
106
120
|
max: LABEL_POOL_MAX,
|
|
107
121
|
min: LABEL_POOL_MIN,
|
|
108
|
-
clear: () => labelPool
|
|
122
|
+
clear: () => labelPool?.reset() // For font change compatibility
|
|
109
123
|
};
|
|
110
124
|
}
|
|
111
125
|
//# sourceMappingURL=headless-pool-for-label.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-pool-for-label.js","sourceRoot":"","sources":["../../server/controllers/headless-pool-for-label.ts"],"names":[],"mappings":";AAAA;;;GAGG;;
|
|
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, onHook, 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/*\n * The pool is made when the server has started, not when this module is loaded.\n *\n * With min=1 the pool builds a browser as soon as it exists, and the page setup reads fonts from the database and\n * loads a page from this same server. At module load neither is ready: shell's databaseInitializer is still running\n * synchronize(), and on sqlite every query shares the one query runner. The fonts query's release() cleared the\n * runner's loaded-table list mid-synchronize, which then created env_vars again and died on 'already exists' —\n * plant did not start after 10.1.38 (seen 2026-09-24; it showed then because ops-master made synchronize rebuild\n * five tables, a longer window). 'bootstrap-module-start' comes after the database and after the server listens.\n *\n * A caller that needs a label before that — none should, since routes are served only after it — still gets a pool.\n */\nlet labelPool: ReturnType<typeof getOrCreateHeadlessPool> | undefined\n\nfunction pool() {\n labelPool ??= getOrCreateHeadlessPool('label', {\n min: LABEL_POOL_MIN,\n max: LABEL_POOL_MAX,\n args: [...HEADLESS_POOL_ARGUMENT_SETS.basic, ...HEADLESS_POOL_ARGUMENT_SETS.keychain_safe],\n acquireTimeoutMillis: 15000,\n testOnBorrow: true,\n enableCleanup: true,\n customSetup: setupLabelPage\n })\n return labelPool\n}\n\nonHook('bootstrap-module-start', () => {\n pool()\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; a pool not made yet will read the new fonts when it is.\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: () => pool().acquire(),\n release: (resource: any) => pool().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}"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The label pool is made after the server starts, not when the module loads.
|
|
3
|
+
*
|
|
4
|
+
* With min=1 a pool builds a browser as soon as it exists, and that setup reads fonts from the database. At module
|
|
5
|
+
* load the database is still being synchronised; on sqlite that query shares synchronize()'s one query runner and
|
|
6
|
+
* broke it — plant did not start after 10.1.38 (2026-09-24). 'bootstrap-module-start' follows the database.
|
|
7
|
+
*/
|
|
8
|
+
declare const created: any[];
|
|
9
|
+
declare const hooks: Record<string, Function[]>;
|
|
10
|
+
declare const load: () => typeof import("./headless-pool-for-label");
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The label pool is made after the server starts, not when the module loads.
|
|
3
|
+
*
|
|
4
|
+
* With min=1 a pool builds a browser as soon as it exists, and that setup reads fonts from the database. At module
|
|
5
|
+
* load the database is still being synchronised; on sqlite that query shares synchronize()'s one query runner and
|
|
6
|
+
* broke it — plant did not start after 10.1.38 (2026-09-24). 'bootstrap-module-start' follows the database.
|
|
7
|
+
*/
|
|
8
|
+
const created = [];
|
|
9
|
+
const hooks = {};
|
|
10
|
+
jest.mock('@things-factory/shell', () => ({
|
|
11
|
+
HEADLESS_POOL_ARGUMENT_SETS: { basic: [], keychain_safe: [] },
|
|
12
|
+
getOrCreateHeadlessPool: jest.fn((name, config) => {
|
|
13
|
+
const pool = { name, config, acquire: jest.fn(async () => 'page'), release: jest.fn(), reset: jest.fn() };
|
|
14
|
+
created.push(pool);
|
|
15
|
+
return pool;
|
|
16
|
+
}),
|
|
17
|
+
onHook: (name, handler) => (hooks[name] ??= []).push(handler),
|
|
18
|
+
pubsub: { subscribe: () => ({ [Symbol.asyncIterator]: () => ({ next: () => new Promise(() => { }) }) }) }
|
|
19
|
+
}));
|
|
20
|
+
jest.mock('./fonts', () => ({ fonts: jest.fn(async () => [[], '']) }));
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.resetModules();
|
|
23
|
+
created.length = 0;
|
|
24
|
+
for (const key of Object.keys(hooks))
|
|
25
|
+
delete hooks[key];
|
|
26
|
+
});
|
|
27
|
+
const load = () => require('./headless-pool-for-label');
|
|
28
|
+
test('loading the module makes no pool — nothing touches the database while it is being synchronised', () => {
|
|
29
|
+
load();
|
|
30
|
+
expect(created).toHaveLength(0);
|
|
31
|
+
});
|
|
32
|
+
test('the pool is made when the server has started', () => {
|
|
33
|
+
load();
|
|
34
|
+
expect(hooks['bootstrap-module-start']).toHaveLength(1);
|
|
35
|
+
hooks['bootstrap-module-start'][0]({});
|
|
36
|
+
expect(created).toHaveLength(1);
|
|
37
|
+
expect(created[0].name).toBe('label');
|
|
38
|
+
});
|
|
39
|
+
test('one pool, however it is first reached', async () => {
|
|
40
|
+
const { getHeadlessPool } = load();
|
|
41
|
+
await getHeadlessPool().acquire();
|
|
42
|
+
hooks['bootstrap-module-start'][0]({});
|
|
43
|
+
await getHeadlessPool().acquire();
|
|
44
|
+
expect(created).toHaveLength(1);
|
|
45
|
+
expect(created[0].acquire).toHaveBeenCalledTimes(2);
|
|
46
|
+
});
|
|
47
|
+
test('clearing before the pool exists does nothing and does not make one', async () => {
|
|
48
|
+
const { getHeadlessPool } = load();
|
|
49
|
+
await getHeadlessPool().clear();
|
|
50
|
+
expect(created).toHaveLength(0);
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=headless-pool-for-label.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-pool-for-label.test.js","sourceRoot":"","sources":["../../server/controllers/headless-pool-for-label.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The label pool is made after the server starts, not when the module loads.\n *\n * With min=1 a pool builds a browser as soon as it exists, and that setup reads fonts from the database. At module\n * load the database is still being synchronised; on sqlite that query shares synchronize()'s one query runner and\n * broke it — plant did not start after 10.1.38 (2026-09-24). 'bootstrap-module-start' follows the database.\n */\nconst created: any[] = []\nconst hooks: Record<string, Function[]> = {}\n\njest.mock('@things-factory/shell', () => ({\n HEADLESS_POOL_ARGUMENT_SETS: { basic: [], keychain_safe: [] },\n getOrCreateHeadlessPool: jest.fn((name: string, config: any) => {\n const pool = { name, config, acquire: jest.fn(async () => 'page'), release: jest.fn(), reset: jest.fn() }\n created.push(pool)\n return pool\n }),\n onHook: (name: string, handler: Function) => (hooks[name] ??= []).push(handler),\n pubsub: { subscribe: () => ({ [Symbol.asyncIterator]: () => ({ next: () => new Promise(() => {}) }) }) }\n}))\njest.mock('./fonts', () => ({ fonts: jest.fn(async () => [[], '']) }))\n\nbeforeEach(() => {\n jest.resetModules()\n created.length = 0\n for (const key of Object.keys(hooks)) delete hooks[key]\n})\n\nconst load = () => require('./headless-pool-for-label') as typeof import('./headless-pool-for-label')\n\ntest('loading the module makes no pool — nothing touches the database while it is being synchronised', () => {\n load()\n expect(created).toHaveLength(0)\n})\n\ntest('the pool is made when the server has started', () => {\n load()\n expect(hooks['bootstrap-module-start']).toHaveLength(1)\n\n hooks['bootstrap-module-start'][0]({})\n\n expect(created).toHaveLength(1)\n expect(created[0].name).toBe('label')\n})\n\ntest('one pool, however it is first reached', async () => {\n const { getHeadlessPool } = load()\n\n await getHeadlessPool().acquire()\n hooks['bootstrap-module-start'][0]({})\n await getHeadlessPool().acquire()\n\n expect(created).toHaveLength(1)\n expect(created[0].acquire).toHaveBeenCalledTimes(2)\n})\n\ntest('clearing before the pool exists does nothing and does not make one', async () => {\n const { getHeadlessPool } = load()\n await getHeadlessPool().clear()\n expect(created).toHaveLength(0)\n})\n"]}
|