@things-factory/shell 9.0.25 → 9.0.34

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 (29) hide show
  1. package/dist-server/process-cleaner.d.ts +33 -0
  2. package/dist-server/process-cleaner.js +70 -75
  3. package/dist-server/process-cleaner.js.map +1 -1
  4. package/dist-server/pubsub.js +0 -23
  5. package/dist-server/pubsub.js.map +1 -1
  6. package/dist-server/server-dev.js +1 -0
  7. package/dist-server/server-dev.js.map +1 -1
  8. package/dist-server/server.js +1 -0
  9. package/dist-server/server.js.map +1 -1
  10. package/dist-server/tsconfig.tsbuildinfo +1 -1
  11. package/dist-server/utils/headless-pool/browser-factory.d.ts +27 -0
  12. package/dist-server/utils/headless-pool/browser-factory.js +144 -0
  13. package/dist-server/utils/headless-pool/browser-factory.js.map +1 -0
  14. package/dist-server/utils/headless-pool/config.d.ts +25 -0
  15. package/dist-server/utils/headless-pool/config.js +72 -0
  16. package/dist-server/utils/headless-pool/config.js.map +1 -0
  17. package/dist-server/utils/headless-pool/index.d.ts +98 -0
  18. package/dist-server/utils/headless-pool/index.js +131 -0
  19. package/dist-server/utils/headless-pool/index.js.map +1 -0
  20. package/dist-server/utils/headless-pool/pool-manager.d.ts +59 -0
  21. package/dist-server/utils/headless-pool/pool-manager.js +212 -0
  22. package/dist-server/utils/headless-pool/pool-manager.js.map +1 -0
  23. package/dist-server/utils/headless-pool/pool-stats.d.ts +29 -0
  24. package/dist-server/utils/headless-pool/pool-stats.js +80 -0
  25. package/dist-server/utils/headless-pool/pool-stats.js.map +1 -0
  26. package/dist-server/utils/index.d.ts +1 -0
  27. package/dist-server/utils/index.js +1 -0
  28. package/dist-server/utils/index.js.map +1 -1
  29. package/package.json +4 -2
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HeadlessPoolManager = exports.HeadlessPool = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const genericPool = tslib_1.__importStar(require("generic-pool"));
6
+ const env_1 = require("@things-factory/env");
7
+ const config_1 = require("./config");
8
+ const browser_factory_1 = require("./browser-factory");
9
+ const pool_stats_1 = require("./pool-stats");
10
+ /**
11
+ * Individual Pool Instance
12
+ */
13
+ class HeadlessPool {
14
+ constructor(name, config) {
15
+ this.name = name;
16
+ this.config = (0, config_1.mergeConfig)({}, config);
17
+ if (this.config.enableStats) {
18
+ this.stats = new pool_stats_1.PoolStats();
19
+ }
20
+ this.pool = this.createPool();
21
+ }
22
+ createPool() {
23
+ const pool = genericPool.createPool({
24
+ create: () => this.createResource(),
25
+ validate: resource => this.validateResource(resource),
26
+ destroy: resource => this.destroyResource(resource)
27
+ }, {
28
+ min: this.config.min,
29
+ max: this.config.max,
30
+ acquireTimeoutMillis: this.config.acquireTimeoutMillis,
31
+ idleTimeoutMillis: this.config.idleTimeoutMillis,
32
+ testOnBorrow: this.config.testOnBorrow
33
+ });
34
+ // Add monitoring if stats enabled
35
+ if (this.stats) {
36
+ this.addMonitoring(pool);
37
+ }
38
+ return pool;
39
+ }
40
+ async createResource() {
41
+ env_1.logger.info(`Creating browser instance for pool: ${this.name}`);
42
+ if (this.stats) {
43
+ this.stats.incrementCreated();
44
+ }
45
+ if (this.config.customSetup) {
46
+ return browser_factory_1.BrowserFactory.createBrowserWithSetup(this.config, this.config.customSetup);
47
+ }
48
+ return browser_factory_1.BrowserFactory.createBrowser(this.config);
49
+ }
50
+ async validateResource(resource) {
51
+ // For custom setup resources, validate the browser part
52
+ const browser = resource.browser || resource;
53
+ return browser_factory_1.BrowserFactory.validateBrowser(browser);
54
+ }
55
+ async destroyResource(resource) {
56
+ env_1.logger.info(`🗑️ ${this.name}: Starting to destroy resource`);
57
+ if (this.stats) {
58
+ this.stats.incrementDestroyed();
59
+ }
60
+ try {
61
+ // Handle complex resources with custom cleanup
62
+ if (resource.browser && resource.page) {
63
+ env_1.logger.info(`🗂️ ${this.name}: Destroying complex resource (browser + page)`);
64
+ // Complex resource (like from label pool)
65
+ try {
66
+ // Close the page first
67
+ if (!resource.page.isClosed()) {
68
+ await resource.page.close();
69
+ env_1.logger.info(`🗂️ ${this.name}: Page closed successfully`);
70
+ }
71
+ }
72
+ catch (error) {
73
+ env_1.logger.warn(`${this.name}: Failed to close page:`, error);
74
+ }
75
+ // Then destroy the browser
76
+ await browser_factory_1.BrowserFactory.destroyBrowser(resource.browser);
77
+ env_1.logger.info(`🗑️ ${this.name}: Complex resource destroyed`);
78
+ }
79
+ else {
80
+ env_1.logger.info(`🗑️ ${this.name}: Destroying simple browser resource`);
81
+ // Simple browser resource
82
+ const browser = resource.browser || resource;
83
+ await browser_factory_1.BrowserFactory.destroyBrowser(browser);
84
+ env_1.logger.info(`🗑️ ${this.name}: Simple resource destroyed`);
85
+ }
86
+ }
87
+ catch (error) {
88
+ env_1.logger.error(`❌ ${this.name}: Error destroying resource:`, error);
89
+ throw error;
90
+ }
91
+ }
92
+ addMonitoring(pool) {
93
+ const originalAcquire = pool.acquire.bind(pool);
94
+ const originalRelease = pool.release.bind(pool);
95
+ pool.acquire = () => {
96
+ const startTime = Date.now();
97
+ this.stats.incrementAcquired();
98
+ return originalAcquire().then((resource) => {
99
+ const acquisitionTime = Date.now() - startTime;
100
+ this.stats.recordAcquisitionTime(acquisitionTime);
101
+ env_1.logger.info(`${this.name}: Browser acquired in ${acquisitionTime}ms, currently borrowed: ${pool.borrowed}`);
102
+ return resource;
103
+ });
104
+ };
105
+ pool.release = (resource) => {
106
+ this.stats.incrementReleased();
107
+ env_1.logger.info(`${this.name}: Browser released, currently borrowed: ${pool.borrowed - 1}`);
108
+ return originalRelease(resource);
109
+ };
110
+ }
111
+ // Public API
112
+ async acquire() {
113
+ return this.pool.acquire();
114
+ }
115
+ async release(resource) {
116
+ return this.pool.release(resource);
117
+ }
118
+ getStats() {
119
+ if (!this.stats) {
120
+ return null;
121
+ }
122
+ const poolStats = {
123
+ size: this.pool.size,
124
+ available: this.pool.available,
125
+ borrowed: this.pool.borrowed,
126
+ pending: this.pool.pending,
127
+ max: this.pool.max,
128
+ min: this.pool.min
129
+ };
130
+ return {
131
+ poolStatus: {
132
+ ...poolStats,
133
+ utilizationRate: `${this.stats.getUtilizationRate(poolStats.size)}%`
134
+ },
135
+ statistics: {
136
+ ...this.stats.getStats(),
137
+ averageAcquisitionTime: `${this.stats.getAverageAcquisitionTime()}ms`
138
+ },
139
+ health: this.stats.getHealthStatus()
140
+ };
141
+ }
142
+ async cleanup() {
143
+ env_1.logger.info(`🧹 Cleaning up pool: ${this.name}`);
144
+ try {
145
+ await this.pool.drain();
146
+ await this.pool.clear();
147
+ env_1.logger.info(`✅ Pool ${this.name} cleaned up`);
148
+ }
149
+ catch (error) {
150
+ env_1.logger.error(`❌ Pool ${this.name} cleanup failed:`, error);
151
+ }
152
+ }
153
+ async reset() {
154
+ await this.cleanup();
155
+ if (this.stats) {
156
+ this.stats.reset();
157
+ }
158
+ this.pool = this.createPool();
159
+ }
160
+ }
161
+ exports.HeadlessPool = HeadlessPool;
162
+ /**
163
+ * Global Pool Manager/Registry
164
+ */
165
+ class HeadlessPoolManager {
166
+ static { this.pools = new Map(); }
167
+ static createPool(name, config = {}) {
168
+ if (this.pools.has(name)) {
169
+ throw new Error(`Pool with name '${name}' already exists`);
170
+ }
171
+ const pool = new HeadlessPool(name, config);
172
+ this.pools.set(name, pool);
173
+ env_1.logger.info(`Headless pool '${name}' created`);
174
+ return pool;
175
+ }
176
+ static getPool(name) {
177
+ const pool = this.pools.get(name);
178
+ if (!pool) {
179
+ throw new Error(`Pool with name '${name}' not found`);
180
+ }
181
+ return pool;
182
+ }
183
+ static hasPool(name) {
184
+ return this.pools.has(name);
185
+ }
186
+ static getAllPoolStats() {
187
+ const stats = {};
188
+ for (const [name, pool] of this.pools) {
189
+ stats[name] = pool.getStats();
190
+ }
191
+ return stats;
192
+ }
193
+ static async cleanupAll() {
194
+ env_1.logger.info(`🧹 Cleaning up all headless pools (${this.pools.size} pools)`);
195
+ const cleanupPromises = Array.from(this.pools.values()).map(pool => pool.cleanup().catch(error => env_1.logger.error('Pool cleanup failed:', error)));
196
+ await Promise.all(cleanupPromises);
197
+ env_1.logger.info('✅ All headless pools cleaned up');
198
+ }
199
+ static async resetPool(name) {
200
+ const pool = this.getPool(name);
201
+ await pool.reset();
202
+ env_1.logger.info(`Pool '${name}' has been reset`);
203
+ }
204
+ static removePool(name) {
205
+ if (this.pools.has(name)) {
206
+ this.pools.delete(name);
207
+ env_1.logger.info(`Pool '${name}' removed from registry`);
208
+ }
209
+ }
210
+ }
211
+ exports.HeadlessPoolManager = HeadlessPoolManager;
212
+ //# sourceMappingURL=pool-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool-manager.js","sourceRoot":"","sources":["../../../server/utils/headless-pool/pool-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import * as genericPool from 'generic-pool'\nimport { logger } from '@things-factory/env'\n\nimport { HeadlessPoolConfig, mergeConfig } from './config'\nimport { BrowserFactory } from './browser-factory'\nimport { PoolStats } from './pool-stats'\n\n/**\n * Individual Pool Instance\n */\nexport class HeadlessPool {\n private pool: genericPool.Pool<any>\n private stats?: PoolStats\n private config: HeadlessPoolConfig\n private name: string\n\n constructor(name: string, config: HeadlessPoolConfig) {\n this.name = name\n this.config = mergeConfig({}, config)\n\n if (this.config.enableStats) {\n this.stats = new PoolStats()\n }\n\n this.pool = this.createPool()\n }\n\n private createPool(): genericPool.Pool<any> {\n const pool = genericPool.createPool(\n {\n create: () => this.createResource(),\n validate: resource => this.validateResource(resource),\n destroy: resource => this.destroyResource(resource)\n },\n {\n min: this.config.min!,\n max: this.config.max!,\n acquireTimeoutMillis: this.config.acquireTimeoutMillis!,\n idleTimeoutMillis: this.config.idleTimeoutMillis,\n testOnBorrow: this.config.testOnBorrow!\n }\n )\n\n // Add monitoring if stats enabled\n if (this.stats) {\n this.addMonitoring(pool)\n }\n\n return pool\n }\n\n private async createResource(): Promise<any> {\n logger.info(`Creating browser instance for pool: ${this.name}`)\n\n if (this.stats) {\n this.stats.incrementCreated()\n }\n\n if (this.config.customSetup) {\n return BrowserFactory.createBrowserWithSetup(this.config, this.config.customSetup)\n }\n\n return BrowserFactory.createBrowser(this.config)\n }\n\n private async validateResource(resource: any): Promise<boolean> {\n // For custom setup resources, validate the browser part\n const browser = resource.browser || resource\n return BrowserFactory.validateBrowser(browser)\n }\n\n private async destroyResource(resource: any): Promise<void> {\n logger.info(`🗑️ ${this.name}: Starting to destroy resource`)\n\n if (this.stats) {\n this.stats.incrementDestroyed()\n }\n\n try {\n // Handle complex resources with custom cleanup\n if (resource.browser && resource.page) {\n logger.info(`🗂️ ${this.name}: Destroying complex resource (browser + page)`)\n // Complex resource (like from label pool)\n try {\n // Close the page first\n if (!resource.page.isClosed()) {\n await resource.page.close()\n logger.info(`🗂️ ${this.name}: Page closed successfully`)\n }\n } catch (error) {\n logger.warn(`${this.name}: Failed to close page:`, error)\n }\n\n // Then destroy the browser\n await BrowserFactory.destroyBrowser(resource.browser)\n logger.info(`🗑️ ${this.name}: Complex resource destroyed`)\n } else {\n logger.info(`🗑️ ${this.name}: Destroying simple browser resource`)\n // Simple browser resource\n const browser = resource.browser || resource\n await BrowserFactory.destroyBrowser(browser)\n logger.info(`🗑️ ${this.name}: Simple resource destroyed`)\n }\n } catch (error) {\n logger.error(`❌ ${this.name}: Error destroying resource:`, error)\n throw error\n }\n }\n\n private addMonitoring(pool: genericPool.Pool<any>): void {\n const originalAcquire = pool.acquire.bind(pool)\n const originalRelease = pool.release.bind(pool)\n\n pool.acquire = () => {\n const startTime = Date.now()\n this.stats!.incrementAcquired()\n\n return originalAcquire().then((resource: any) => {\n const acquisitionTime = Date.now() - startTime\n this.stats!.recordAcquisitionTime(acquisitionTime)\n\n logger.info(`${this.name}: Browser acquired in ${acquisitionTime}ms, currently borrowed: ${pool.borrowed}`)\n return resource\n })\n }\n\n pool.release = (resource: any) => {\n this.stats!.incrementReleased()\n logger.info(`${this.name}: Browser released, currently borrowed: ${pool.borrowed - 1}`)\n return originalRelease(resource)\n }\n }\n\n // Public API\n async acquire() {\n return this.pool.acquire()\n }\n\n async release(resource: any) {\n return this.pool.release(resource)\n }\n\n getStats() {\n if (!this.stats) {\n return null\n }\n\n const poolStats = {\n size: this.pool.size,\n available: this.pool.available,\n borrowed: this.pool.borrowed,\n pending: this.pool.pending,\n max: this.pool.max,\n min: this.pool.min\n }\n\n return {\n poolStatus: {\n ...poolStats,\n utilizationRate: `${this.stats.getUtilizationRate(poolStats.size)}%`\n },\n statistics: {\n ...this.stats.getStats(),\n averageAcquisitionTime: `${this.stats.getAverageAcquisitionTime()}ms`\n },\n health: this.stats.getHealthStatus()\n }\n }\n\n async cleanup() {\n logger.info(`🧹 Cleaning up pool: ${this.name}`)\n\n try {\n await this.pool.drain()\n await this.pool.clear()\n logger.info(`✅ Pool ${this.name} cleaned up`)\n } catch (error) {\n logger.error(`❌ Pool ${this.name} cleanup failed:`, error)\n }\n }\n\n\n async reset() {\n await this.cleanup()\n if (this.stats) {\n this.stats.reset()\n }\n this.pool = this.createPool()\n }\n}\n\n/**\n * Global Pool Manager/Registry\n */\nexport class HeadlessPoolManager {\n private static pools = new Map<string, HeadlessPool>()\n\n static createPool(name: string, config: HeadlessPoolConfig = {}): HeadlessPool {\n if (this.pools.has(name)) {\n throw new Error(`Pool with name '${name}' already exists`)\n }\n\n const pool = new HeadlessPool(name, config)\n this.pools.set(name, pool)\n\n logger.info(`Headless pool '${name}' created`)\n return pool\n }\n\n static getPool(name: string): HeadlessPool {\n const pool = this.pools.get(name)\n if (!pool) {\n throw new Error(`Pool with name '${name}' not found`)\n }\n return pool\n }\n\n static hasPool(name: string): boolean {\n return this.pools.has(name)\n }\n\n static getAllPoolStats() {\n const stats: Record<string, any> = {}\n\n for (const [name, pool] of this.pools) {\n stats[name] = pool.getStats()\n }\n\n return stats\n }\n\n static async cleanupAll(): Promise<void> {\n logger.info(`🧹 Cleaning up all headless pools (${this.pools.size} pools)`)\n\n const cleanupPromises = Array.from(this.pools.values()).map(pool =>\n pool.cleanup().catch(error => logger.error('Pool cleanup failed:', error))\n )\n\n await Promise.all(cleanupPromises)\n logger.info('✅ All headless pools cleaned up')\n }\n\n static async resetPool(name: string): Promise<void> {\n const pool = this.getPool(name)\n await pool.reset()\n logger.info(`Pool '${name}' has been reset`)\n }\n\n static removePool(name: string): void {\n if (this.pools.has(name)) {\n this.pools.delete(name)\n logger.info(`Pool '${name}' removed from registry`)\n }\n }\n}\n"]}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Pool Statistics Tracker
3
+ */
4
+ export declare class PoolStats {
5
+ private stats;
6
+ private updateActivity;
7
+ incrementCreated(): void;
8
+ incrementDestroyed(): void;
9
+ incrementAcquired(): void;
10
+ incrementReleased(): void;
11
+ recordAcquisitionTime(time: number): void;
12
+ getAverageAcquisitionTime(): number;
13
+ getStats(): {
14
+ totalCreated: number;
15
+ totalDestroyed: number;
16
+ totalAcquired: number;
17
+ totalReleased: number;
18
+ currentlyAcquired: number;
19
+ acquisitionTimes: number[];
20
+ lastActivity: string | null;
21
+ };
22
+ reset(): void;
23
+ getUtilizationRate(poolSize: number): string;
24
+ getHealthStatus(): {
25
+ status: string;
26
+ message: string;
27
+ leakDetection: string;
28
+ };
29
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PoolStats = void 0;
4
+ /**
5
+ * Pool Statistics Tracker
6
+ */
7
+ class PoolStats {
8
+ constructor() {
9
+ this.stats = {
10
+ totalCreated: 0,
11
+ totalDestroyed: 0,
12
+ totalAcquired: 0,
13
+ totalReleased: 0,
14
+ currentlyAcquired: 0,
15
+ acquisitionTimes: [],
16
+ lastActivity: null
17
+ };
18
+ }
19
+ updateActivity() {
20
+ this.stats.lastActivity = new Date().toISOString();
21
+ }
22
+ incrementCreated() {
23
+ this.stats.totalCreated++;
24
+ this.updateActivity();
25
+ }
26
+ incrementDestroyed() {
27
+ this.stats.totalDestroyed++;
28
+ this.updateActivity();
29
+ }
30
+ incrementAcquired() {
31
+ this.stats.totalAcquired++;
32
+ this.stats.currentlyAcquired++;
33
+ this.updateActivity();
34
+ }
35
+ incrementReleased() {
36
+ this.stats.totalReleased++;
37
+ this.stats.currentlyAcquired = Math.max(0, this.stats.currentlyAcquired - 1);
38
+ this.updateActivity();
39
+ }
40
+ recordAcquisitionTime(time) {
41
+ this.stats.acquisitionTimes.push(time);
42
+ // Keep only recent 100 records
43
+ if (this.stats.acquisitionTimes.length > 100) {
44
+ this.stats.acquisitionTimes.shift();
45
+ }
46
+ }
47
+ getAverageAcquisitionTime() {
48
+ if (this.stats.acquisitionTimes.length === 0)
49
+ return 0;
50
+ return Math.round(this.stats.acquisitionTimes.reduce((a, b) => a + b, 0) / this.stats.acquisitionTimes.length);
51
+ }
52
+ getStats() {
53
+ return { ...this.stats };
54
+ }
55
+ reset() {
56
+ this.stats = {
57
+ totalCreated: 0,
58
+ totalDestroyed: 0,
59
+ totalAcquired: 0,
60
+ totalReleased: 0,
61
+ currentlyAcquired: 0,
62
+ acquisitionTimes: [],
63
+ lastActivity: new Date().toISOString()
64
+ };
65
+ }
66
+ getUtilizationRate(poolSize) {
67
+ return poolSize > 0 ? (this.stats.currentlyAcquired / poolSize * 100).toFixed(1) : '0';
68
+ }
69
+ // Health check for potential leaks
70
+ getHealthStatus() {
71
+ const potentialLeak = this.stats.totalAcquired - this.stats.totalReleased > this.stats.currentlyAcquired;
72
+ return {
73
+ status: potentialLeak ? 'warning' : 'healthy',
74
+ message: potentialLeak ? 'Potential resource leak detected' : 'Pool is healthy',
75
+ leakDetection: potentialLeak ? 'potential leak detected' : 'normal'
76
+ };
77
+ }
78
+ }
79
+ exports.PoolStats = PoolStats;
80
+ //# sourceMappingURL=pool-stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool-stats.js","sourceRoot":"","sources":["../../../server/utils/headless-pool/pool-stats.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pool Statistics Tracker\n */\nexport class PoolStats {\n private stats = {\n totalCreated: 0,\n totalDestroyed: 0,\n totalAcquired: 0,\n totalReleased: 0,\n currentlyAcquired: 0,\n acquisitionTimes: [] as number[],\n lastActivity: null as string | null\n }\n\n private updateActivity() {\n this.stats.lastActivity = new Date().toISOString()\n }\n\n incrementCreated() {\n this.stats.totalCreated++\n this.updateActivity()\n }\n\n incrementDestroyed() {\n this.stats.totalDestroyed++\n this.updateActivity()\n }\n\n incrementAcquired() {\n this.stats.totalAcquired++\n this.stats.currentlyAcquired++\n this.updateActivity()\n }\n\n incrementReleased() {\n this.stats.totalReleased++\n this.stats.currentlyAcquired = Math.max(0, this.stats.currentlyAcquired - 1)\n this.updateActivity()\n }\n\n recordAcquisitionTime(time: number) {\n this.stats.acquisitionTimes.push(time)\n // Keep only recent 100 records\n if (this.stats.acquisitionTimes.length > 100) {\n this.stats.acquisitionTimes.shift()\n }\n }\n\n getAverageAcquisitionTime(): number {\n if (this.stats.acquisitionTimes.length === 0) return 0\n return Math.round(\n this.stats.acquisitionTimes.reduce((a, b) => a + b, 0) / this.stats.acquisitionTimes.length\n )\n }\n\n getStats() {\n return { ...this.stats }\n }\n\n reset() {\n this.stats = {\n totalCreated: 0,\n totalDestroyed: 0,\n totalAcquired: 0,\n totalReleased: 0,\n currentlyAcquired: 0,\n acquisitionTimes: [],\n lastActivity: new Date().toISOString()\n }\n }\n\n getUtilizationRate(poolSize: number): string {\n return poolSize > 0 ? (this.stats.currentlyAcquired / poolSize * 100).toFixed(1) : '0'\n }\n\n // Health check for potential leaks\n getHealthStatus() {\n const potentialLeak = this.stats.totalAcquired - this.stats.totalReleased > this.stats.currentlyAcquired\n return {\n status: potentialLeak ? 'warning' : 'healthy',\n message: potentialLeak ? 'Potential resource leak detected' : 'Pool is healthy',\n leakDetection: potentialLeak ? 'potential leak detected' : 'normal'\n }\n }\n}"]}
@@ -6,3 +6,4 @@ export * from './publish-progress.js';
6
6
  export * from './get-query-builder-from-list-params.js';
7
7
  export * from './list-param-adjuster.js';
8
8
  export * from './get-times-for-period.js';
9
+ export * from './headless-pool/index.js';
@@ -9,4 +9,5 @@ tslib_1.__exportStar(require("./publish-progress.js"), exports);
9
9
  tslib_1.__exportStar(require("./get-query-builder-from-list-params.js"), exports);
10
10
  tslib_1.__exportStar(require("./list-param-adjuster.js"), exports);
11
11
  tslib_1.__exportStar(require("./get-times-for-period.js"), exports);
12
+ tslib_1.__exportStar(require("./headless-pool/index.js"), exports);
12
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/utils/index.ts"],"names":[],"mappings":";;;AAAA,0DAA+B;AAC/B,iEAAsC;AACtC,kEAAuC;AACvC,qEAA0C;AAC1C,gEAAqC;AACrC,kFAAuD;AACvD,mEAAwC;AACxC,oEAAyC","sourcesContent":["export * from './get-domain.js'\nexport * from './condition-builder.js'\nexport * from './list-query-builder.js'\nexport * from './list-params-converter.js'\nexport * from './publish-progress.js'\nexport * from './get-query-builder-from-list-params.js'\nexport * from './list-param-adjuster.js'\nexport * from './get-times-for-period.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/utils/index.ts"],"names":[],"mappings":";;;AAAA,0DAA+B;AAC/B,iEAAsC;AACtC,kEAAuC;AACvC,qEAA0C;AAC1C,gEAAqC;AACrC,kFAAuD;AACvD,mEAAwC;AACxC,oEAAyC;AACzC,mEAAwC","sourcesContent":["export * from './get-domain.js'\nexport * from './condition-builder.js'\nexport * from './list-query-builder.js'\nexport * from './list-params-converter.js'\nexport * from './publish-progress.js'\nexport * from './get-query-builder-from-list-params.js'\nexport * from './list-param-adjuster.js'\nexport * from './get-times-for-period.js'\nexport * from './headless-pool/index.js'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/shell",
3
- "version": "9.0.25",
3
+ "version": "9.0.34",
4
4
  "description": "Core module for framework",
5
5
  "bin": {
6
6
  "things-factory": "bin/things-factory",
@@ -72,6 +72,7 @@
72
72
  "core-js": "^3.26.0",
73
73
  "debug": "^4.1.1",
74
74
  "firebase": "^9.14.0",
75
+ "generic-pool": "^3.8.2",
75
76
  "glob": "^11.0.1",
76
77
  "graphql": "^16.5.0",
77
78
  "graphql-scalars": "^1.22.4",
@@ -105,6 +106,7 @@
105
106
  "npm-license-crawler": "^0.2.1",
106
107
  "pluralize": "^8.0.0",
107
108
  "promises-all": "^1.0.0",
109
+ "puppeteer": "^24.5.0",
108
110
  "pwa-helpers": "^0.9.1",
109
111
  "react": "^18.2.0",
110
112
  "react-dom": "^18.2.0",
@@ -129,5 +131,5 @@
129
131
  "pg": "^8.7.3",
130
132
  "sqlite3": "^5.0.8"
131
133
  },
132
- "gitHead": "f15d38b26438fe7e14edefdeb0b752ce9fe5fa83"
134
+ "gitHead": "b85ff377bc6ca4a81e53342b20087d4e0a286772"
133
135
  }