@magek/server 0.0.6 → 0.0.8

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/index.js CHANGED
@@ -46,42 +46,37 @@ exports.ServerRuntime = {
46
46
  },
47
47
  sensor: {
48
48
  databaseEventsHealthDetails: (config) => {
49
- var _a;
50
49
  // Delegate to event store adapter health check if available
51
- if ((_a = config.eventStoreAdapter) === null || _a === void 0 ? void 0 : _a.healthCheck) {
50
+ if (config.eventStoreAdapter?.healthCheck) {
52
51
  return config.eventStoreAdapter.healthCheck.details(config);
53
52
  }
54
53
  throw new Error('No event store adapter configured for health checks');
55
54
  },
56
55
  databaseReadModelsHealthDetails: (config) => {
57
- var _a;
58
56
  // Delegate to read model store adapter health check if available
59
- if ((_a = config.readModelStoreAdapter) === null || _a === void 0 ? void 0 : _a.healthCheck) {
57
+ if (config.readModelStoreAdapter?.healthCheck) {
60
58
  return config.readModelStoreAdapter.healthCheck.details(config);
61
59
  }
62
60
  throw new Error('No read model store adapter configured for health checks');
63
61
  },
64
62
  isDatabaseEventUp: (config) => {
65
- var _a;
66
63
  // Delegate to event store adapter health check if available
67
- if ((_a = config.eventStoreAdapter) === null || _a === void 0 ? void 0 : _a.healthCheck) {
64
+ if (config.eventStoreAdapter?.healthCheck) {
68
65
  return config.eventStoreAdapter.healthCheck.isUp(config);
69
66
  }
70
67
  return Promise.resolve(false);
71
68
  },
72
69
  areDatabaseReadModelsUp: (config) => {
73
- var _a;
74
70
  // Delegate to read model store adapter health check if available
75
- if ((_a = config.readModelStoreAdapter) === null || _a === void 0 ? void 0 : _a.healthCheck) {
71
+ if (config.readModelStoreAdapter?.healthCheck) {
76
72
  return config.readModelStoreAdapter.healthCheck.isUp(config);
77
73
  }
78
74
  return Promise.resolve(false);
79
75
  },
80
76
  databaseUrls: (config) => {
81
- var _a, _b, _c, _d, _e, _f;
82
77
  // Get URLs from both event store and read model store adapters
83
- const eventUrls = (_c = (_b = (_a = config.eventStoreAdapter) === null || _a === void 0 ? void 0 : _a.healthCheck) === null || _b === void 0 ? void 0 : _b.urls(config)) !== null && _c !== void 0 ? _c : Promise.resolve([]);
84
- const readModelUrls = (_f = (_e = (_d = config.readModelStoreAdapter) === null || _d === void 0 ? void 0 : _d.healthCheck) === null || _e === void 0 ? void 0 : _e.urls(config)) !== null && _f !== void 0 ? _f : Promise.resolve([]);
78
+ const eventUrls = config.eventStoreAdapter?.healthCheck?.urls(config) ?? Promise.resolve([]);
79
+ const readModelUrls = config.readModelStoreAdapter?.healthCheck?.urls(config) ?? Promise.resolve([]);
85
80
  return Promise.all([eventUrls, readModelUrls]).then(([events, readModels]) => [
86
81
  ...events,
87
82
  ...readModels
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GraphQLController = void 0;
4
4
  const http_1 = require("../http");
5
5
  class GraphQLController {
6
+ graphQLService;
6
7
  constructor(graphQLService) {
7
8
  this.graphQLService = graphQLService;
8
9
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HealthController = void 0;
4
4
  const http_1 = require("../http");
5
5
  class HealthController {
6
+ healthService;
6
7
  constructor(healthService) {
7
8
  this.healthService = healthService;
8
9
  }
@@ -0,0 +1,18 @@
1
+ import { UserApp, MagekConfig } from '@magek/common';
2
+ /**
3
+ * Starts the event polling loop.
4
+ * Polls for unprocessed events and dispatches them via eventDispatcher.
5
+ *
6
+ * @param userApp - The user's Magek application module
7
+ * @param config - The Magek configuration object
8
+ */
9
+ export declare function startEventPolling(userApp: UserApp, config: MagekConfig): void;
10
+ /**
11
+ * Waits for the current poll cycle to complete.
12
+ * Exported for tests to ensure async poll callbacks finish before assertions.
13
+ */
14
+ export declare function waitForCurrentPoll(): Promise<void>;
15
+ /**
16
+ * Stops the event polling loop.
17
+ */
18
+ export declare function stopEventPolling(): void;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startEventPolling = startEventPolling;
4
+ exports.waitForCurrentPoll = waitForCurrentPoll;
5
+ exports.stopEventPolling = stopEventPolling;
6
+ const common_1 = require("@magek/common");
7
+ let pollingTimer = null;
8
+ let currentPollPromise = null;
9
+ /**
10
+ * Starts the event polling loop.
11
+ * Polls for unprocessed events and dispatches them via eventDispatcher.
12
+ *
13
+ * @param userApp - The user's Magek application module
14
+ * @param config - The Magek configuration object
15
+ */
16
+ function startEventPolling(userApp, config) {
17
+ const logger = (0, common_1.getLogger)(config, 'EventPoller');
18
+ const intervalMs = config.eventPollingIntervalMs;
19
+ logger.info(`Starting event polling every ${intervalMs}ms (batch size: ${config.eventProcessingBatchSize})`);
20
+ pollingTimer = setInterval(() => {
21
+ // Skip if previous poll is still running to prevent overlapping executions
22
+ if (currentPollPromise) {
23
+ return;
24
+ }
25
+ // Store the promise so tests can await it and we can prevent overlap
26
+ currentPollPromise = pollAndProcessEvents(userApp, config, logger).finally(() => {
27
+ currentPollPromise = null;
28
+ });
29
+ }, intervalMs);
30
+ }
31
+ /**
32
+ * Waits for the current poll cycle to complete.
33
+ * Exported for tests to ensure async poll callbacks finish before assertions.
34
+ */
35
+ async function waitForCurrentPoll() {
36
+ if (currentPollPromise) {
37
+ await currentPollPromise;
38
+ }
39
+ }
40
+ /**
41
+ * Stops the event polling loop.
42
+ */
43
+ function stopEventPolling() {
44
+ if (pollingTimer) {
45
+ clearInterval(pollingTimer);
46
+ pollingTimer = null;
47
+ }
48
+ currentPollPromise = null;
49
+ }
50
+ async function pollAndProcessEvents(userApp, config, logger) {
51
+ const eventStore = config.eventStore;
52
+ // Check if adapter supports async processing
53
+ if (!eventStore.fetchUnprocessedEvents || !eventStore.markEventProcessed) {
54
+ return;
55
+ }
56
+ try {
57
+ const events = await eventStore.fetchUnprocessedEvents(config);
58
+ if (events.length === 0) {
59
+ return;
60
+ }
61
+ logger.debug(`Processing ${events.length} events`);
62
+ // Process each event individually
63
+ for (const event of events) {
64
+ try {
65
+ // Dispatch the event
66
+ await userApp.eventDispatcher([event]);
67
+ // Mark as processed only after successful dispatch
68
+ if (event.id) {
69
+ await eventStore.markEventProcessed(config, event.id);
70
+ }
71
+ }
72
+ catch (error) {
73
+ logger.error(`Error processing event ${event.id}:`, error);
74
+ // Stop processing on first error to maintain order
75
+ break;
76
+ }
77
+ }
78
+ }
79
+ catch (error) {
80
+ logger.error('Error during event polling:', error);
81
+ }
82
+ }
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalTestHelper = void 0;
4
4
  const local_queries_1 = require("./local-queries");
5
5
  class LocalTestHelper {
6
+ outputs;
7
+ queries;
6
8
  constructor(outputs, queries) {
7
9
  this.outputs = outputs;
8
10
  this.queries = queries;
@@ -5,9 +5,7 @@ exports.WebSocketRegistry = void 0;
5
5
  * Registry to manage active WebSocket connections
6
6
  */
7
7
  class WebSocketRegistry {
8
- constructor() {
9
- this.connections = new Map();
10
- }
8
+ connections = new Map();
11
9
  /**
12
10
  * Add a connection to the registry
13
11
  */
@@ -0,0 +1,2 @@
1
+ export declare const MAGEK_LOCAL_PORT = "INTERNAL_LOCAL_PORT";
2
+ export declare const localPort: () => string;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.localPort = exports.MAGEK_LOCAL_PORT = void 0;
4
+ exports.MAGEK_LOCAL_PORT = 'INTERNAL_LOCAL_PORT';
5
+ const localPort = () => process.env[exports.MAGEK_LOCAL_PORT] || '3000';
6
+ exports.localPort = localPort;
@@ -13,16 +13,16 @@ function webSocketMessageToEnvelope(config, webSocketRequest, requestID) {
13
13
  logger.debug('Received WebSocket GraphQL request: ', webSocketRequest);
14
14
  let eventType = 'MESSAGE';
15
15
  const incomingMessage = webSocketRequest.incomingMessage;
16
- const headers = incomingMessage === null || incomingMessage === void 0 ? void 0 : incomingMessage.headers;
16
+ const headers = incomingMessage?.headers;
17
17
  const data = webSocketRequest.data;
18
18
  try {
19
19
  const connectionContext = webSocketRequest.connectionContext;
20
- eventType = connectionContext === null || connectionContext === void 0 ? void 0 : connectionContext.eventType;
20
+ eventType = connectionContext?.eventType;
21
21
  return {
22
22
  requestID,
23
23
  eventType,
24
- connectionID: connectionContext === null || connectionContext === void 0 ? void 0 : connectionContext.connectionId.toString(),
25
- token: Array.isArray(headers === null || headers === void 0 ? void 0 : headers.authorization) ? headers.authorization[0] : headers === null || headers === void 0 ? void 0 : headers.authorization,
24
+ connectionID: connectionContext?.connectionId.toString(),
25
+ token: Array.isArray(headers?.authorization) ? headers.authorization[0] : headers?.authorization,
26
26
  value: data,
27
27
  context: {
28
28
  request: {
@@ -60,7 +60,7 @@ function httpMessageToEnvelope(config, httpRequest, requestId) {
60
60
  connectionID: undefined,
61
61
  requestID: requestId,
62
62
  eventType: eventType,
63
- token: Array.isArray(headers === null || headers === void 0 ? void 0 : headers.authorization) ? headers.authorization[0] : headers === null || headers === void 0 ? void 0 : headers.authorization,
63
+ token: Array.isArray(headers?.authorization) ? headers.authorization[0] : headers?.authorization,
64
64
  value: data,
65
65
  context: {
66
66
  request: {
@@ -8,6 +8,7 @@ exports.isGraphQLFunctionUp = isGraphQLFunctionUp;
8
8
  exports.rawRequestToSensorHealth = rawRequestToSensorHealth;
9
9
  const paths_1 = require("../paths");
10
10
  const common_1 = require("@magek/common");
11
+ const internal_info_1 = require("../internal-info");
11
12
  const fs_1 = require("fs");
12
13
  async function databaseUrl() {
13
14
  return [paths_1.eventsDatabase, paths_1.readModelsDatabase];
@@ -15,11 +16,11 @@ async function databaseUrl() {
15
16
  async function countAll(database) {
16
17
  await database.loadDatabaseAsync();
17
18
  const count = await database.countAsync({});
18
- return count !== null && count !== void 0 ? count : 0;
19
+ return count ?? 0;
19
20
  }
20
21
  async function graphqlFunctionUrl() {
21
22
  try {
22
- const port = (0, common_1.localPort)();
23
+ const port = (0, internal_info_1.localPort)();
23
24
  return `http://localhost:${port}/graphql`;
24
25
  }
25
26
  catch (e) {
@@ -65,6 +66,6 @@ function rawRequestToSensorHealth(rawRequest) {
65
66
  rawContext: rawRequest,
66
67
  },
67
68
  componentPath: componentPath,
68
- token: Array.isArray(headers === null || headers === void 0 ? void 0 : headers.authorization) ? headers.authorization[0] : headers === null || headers === void 0 ? void 0 : headers.authorization,
69
+ token: Array.isArray(headers?.authorization) ? headers.authorization[0] : headers?.authorization,
69
70
  };
70
71
  }
package/dist/server.js CHANGED
@@ -13,6 +13,7 @@ const health_controller_1 = require("./infrastructure/controllers/health-control
13
13
  const websocket_registry_1 = require("./infrastructure/websocket-registry");
14
14
  const http_1 = require("./infrastructure/http");
15
15
  const scheduler_1 = require("./infrastructure/scheduler");
16
+ const event_poller_1 = require("./infrastructure/event-poller");
16
17
  // Global WebSocket registry instance
17
18
  let globalWebSocketRegistry;
18
19
  /**
@@ -191,6 +192,11 @@ async function createServer(userApp, options = {}) {
191
192
  const config = userApp.Magek.config;
192
193
  if (config) {
193
194
  (0, scheduler_1.configureScheduler)(config, userApp);
195
+ (0, event_poller_1.startEventPolling)(userApp, config);
196
+ // Clean up event polling when server shuts down
197
+ fastify.addHook('onClose', async () => {
198
+ (0, event_poller_1.stopEventPolling)();
199
+ });
194
200
  }
195
201
  await fastify.ready();
196
202
  return fastify;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GraphQLService = void 0;
4
4
  class GraphQLService {
5
+ userApp;
5
6
  constructor(userApp) {
6
7
  this.userApp = userApp;
7
8
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HealthService = void 0;
4
4
  class HealthService {
5
+ userApp;
5
6
  constructor(userApp) {
6
7
  this.userApp = userApp;
7
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magek/server",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Debug your Magek projects locally",
5
5
  "keywords": [
6
6
  "server"
@@ -26,20 +26,30 @@
26
26
  "node": ">=22.0.0 <23.0.0"
27
27
  },
28
28
  "dependencies": {
29
- "@magek/common": "^0.0.6",
29
+ "@magek/common": "workspace:^0.0.8",
30
30
  "@fastify/cors": "11.2.0",
31
31
  "@fastify/websocket": "11.2.0",
32
32
  "@seald-io/nedb": "4.1.2",
33
- "fastify": "5.7.1",
34
- "fastify-sse-v2": "4.2.1",
33
+ "fastify": "5.7.3",
34
+ "fastify-sse-v2": "4.2.2",
35
35
  "node-schedule": "2.1.1",
36
36
  "tslib": "2.8.1"
37
37
  },
38
+ "scripts": {
39
+ "format": "prettier --write --ext '.js,.ts' **/*.ts **/*/*.ts",
40
+ "lint:check": "eslint \"**/*.ts\"",
41
+ "lint:fix": "eslint --quiet --fix \"**/*.ts\"",
42
+ "build": "tsc -b tsconfig.json",
43
+ "clean": "rimraf ./dist tsconfig.tsbuildinfo",
44
+ "prepack": "tsc -b tsconfig.json",
45
+ "test:provider-local": "npm run test",
46
+ "test": "tsc --noEmit -p tsconfig.test.json && MAGEK_ENV=test c8 mocha --forbid-only \"test/**/*.test.ts\""
47
+ },
38
48
  "bugs": {
39
49
  "url": "https://github.com/theam/magek/issues"
40
50
  },
41
51
  "devDependencies": {
42
- "@magek/eslint-config": "^0.0.6",
52
+ "@magek/eslint-config": "workspace:^0.0.8",
43
53
  "@types/chai": "5.2.3",
44
54
  "@types/chai-as-promised": "8.0.2",
45
55
  "@types/mocha": "10.0.10",
@@ -57,14 +67,5 @@
57
67
  "sinon-chai": "4.0.1",
58
68
  "tsx": "^4.19.2",
59
69
  "typescript": "5.9.3"
60
- },
61
- "scripts": {
62
- "format": "prettier --write --ext '.js,.ts' **/*.ts **/*/*.ts",
63
- "lint:check": "eslint \"**/*.ts\"",
64
- "lint:fix": "eslint --quiet --fix \"**/*.ts\"",
65
- "build": "tsc -b tsconfig.json",
66
- "clean": "rimraf ./dist tsconfig.tsbuildinfo",
67
- "test:provider-local": "npm run test",
68
- "test": "tsc --noEmit -p tsconfig.test.json && MAGEK_ENV=test c8 mocha --forbid-only \"test/**/*.test.ts\""
69
70
  }
70
- }
71
+ }