blaizejs 0.1.0 → 0.2.0
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/LICENSE +21 -0
- package/README.md +746 -198
- package/dist/index.cjs +314 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +314 -40
- package/dist/index.js.map +1 -1
- package/package.json +23 -24
package/dist/index.cjs
CHANGED
|
@@ -132,8 +132,31 @@ function compose(middlewareStack) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// src/plugins/create.ts
|
|
135
|
-
function create2(name, version, setup,
|
|
136
|
-
|
|
135
|
+
function create2(name, version, setup, defaultOptions = {}) {
|
|
136
|
+
if (!name || typeof name !== "string") {
|
|
137
|
+
throw new Error("Plugin name must be a non-empty string");
|
|
138
|
+
}
|
|
139
|
+
if (!version || typeof version !== "string") {
|
|
140
|
+
throw new Error("Plugin version must be a non-empty string");
|
|
141
|
+
}
|
|
142
|
+
if (typeof setup !== "function") {
|
|
143
|
+
throw new Error("Plugin setup must be a function");
|
|
144
|
+
}
|
|
145
|
+
return function pluginFactory(userOptions) {
|
|
146
|
+
const mergedOptions = { ...defaultOptions, ...userOptions };
|
|
147
|
+
const plugin = {
|
|
148
|
+
name,
|
|
149
|
+
version,
|
|
150
|
+
// The register hook calls the user's setup function
|
|
151
|
+
register: async (app) => {
|
|
152
|
+
const result = await setup(app, mergedOptions);
|
|
153
|
+
if (result && typeof result === "object") {
|
|
154
|
+
Object.assign(plugin, result);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return plugin;
|
|
159
|
+
};
|
|
137
160
|
}
|
|
138
161
|
|
|
139
162
|
// src/router/discovery/finder.ts
|
|
@@ -173,12 +196,18 @@ async function findRouteFiles(routesDir, options = {}) {
|
|
|
173
196
|
return routeFiles;
|
|
174
197
|
}
|
|
175
198
|
function isRouteFile(filename) {
|
|
176
|
-
return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"))
|
|
199
|
+
return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
|
|
177
200
|
}
|
|
178
201
|
|
|
179
202
|
// src/router/discovery/parser.ts
|
|
180
203
|
var path2 = __toESM(require("path"), 1);
|
|
181
204
|
function parseRoutePath(filePath, basePath) {
|
|
205
|
+
if (filePath.startsWith("file://")) {
|
|
206
|
+
filePath = filePath.replace("file://", "");
|
|
207
|
+
}
|
|
208
|
+
if (basePath.startsWith("file://")) {
|
|
209
|
+
basePath = basePath.replace("file://", "");
|
|
210
|
+
}
|
|
182
211
|
const forwardSlashFilePath = filePath.replace(/\\/g, "/");
|
|
183
212
|
const forwardSlashBasePath = basePath.replace(/\\/g, "/");
|
|
184
213
|
const normalizedBasePath = forwardSlashBasePath.endsWith("/") ? forwardSlashBasePath : `${forwardSlashBasePath}/`;
|
|
@@ -712,21 +741,56 @@ function createRouter(options) {
|
|
|
712
741
|
const matcher = createMatcher();
|
|
713
742
|
let initialized = false;
|
|
714
743
|
let initializationPromise = null;
|
|
715
|
-
let
|
|
744
|
+
let _watchers = null;
|
|
745
|
+
const routeSources = /* @__PURE__ */ new Map();
|
|
746
|
+
const routeDirectories = /* @__PURE__ */ new Set([routerOptions.routesDir]);
|
|
747
|
+
function addRouteWithSource(route, source) {
|
|
748
|
+
const existingSources = routeSources.get(route.path) || [];
|
|
749
|
+
if (existingSources.includes(source)) {
|
|
750
|
+
console.warn(`Skipping duplicate route: ${route.path} from ${source}`);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (existingSources.length > 0) {
|
|
754
|
+
const conflictError = new Error(
|
|
755
|
+
`Route conflict for path "${route.path}": already defined in ${existingSources.join(", ")}, now being added from ${source}`
|
|
756
|
+
);
|
|
757
|
+
console.error(conflictError.message);
|
|
758
|
+
throw conflictError;
|
|
759
|
+
}
|
|
760
|
+
routeSources.set(route.path, [...existingSources, source]);
|
|
761
|
+
addRouteInternal(route);
|
|
762
|
+
}
|
|
763
|
+
async function loadRoutesFromDirectory(directory, source, prefix) {
|
|
764
|
+
try {
|
|
765
|
+
const discoveredRoutes = await findRoutes(directory, {
|
|
766
|
+
basePath: routerOptions.basePath
|
|
767
|
+
});
|
|
768
|
+
for (const route of discoveredRoutes) {
|
|
769
|
+
const finalRoute = prefix ? {
|
|
770
|
+
...route,
|
|
771
|
+
path: `${prefix}${route.path}`
|
|
772
|
+
} : route;
|
|
773
|
+
addRouteWithSource(finalRoute, source);
|
|
774
|
+
}
|
|
775
|
+
console.log(
|
|
776
|
+
`Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""}`
|
|
777
|
+
);
|
|
778
|
+
} catch (error) {
|
|
779
|
+
console.error(`Failed to load routes from ${source}:`, error);
|
|
780
|
+
throw error;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
716
783
|
async function initialize() {
|
|
717
784
|
if (initialized || initializationPromise) {
|
|
718
785
|
return initializationPromise;
|
|
719
786
|
}
|
|
720
787
|
initializationPromise = (async () => {
|
|
721
788
|
try {
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
});
|
|
725
|
-
for (const route of discoveredRoutes) {
|
|
726
|
-
addRouteInternal(route);
|
|
789
|
+
for (const directory of routeDirectories) {
|
|
790
|
+
await loadRoutesFromDirectory(directory, directory);
|
|
727
791
|
}
|
|
728
792
|
if (routerOptions.watchMode) {
|
|
729
|
-
|
|
793
|
+
setupWatcherForAllDirectories();
|
|
730
794
|
}
|
|
731
795
|
initialized = true;
|
|
732
796
|
} catch (error) {
|
|
@@ -743,47 +807,81 @@ function createRouter(options) {
|
|
|
743
807
|
matcher.add(route.path, method, methodOptions);
|
|
744
808
|
});
|
|
745
809
|
}
|
|
746
|
-
function
|
|
747
|
-
|
|
748
|
-
ignore: ["node_modules", ".git"],
|
|
810
|
+
function createWatcherCallbacks(directory, source, prefix) {
|
|
811
|
+
return {
|
|
749
812
|
onRouteAdded: (addedRoutes) => {
|
|
750
813
|
console.log(
|
|
751
|
-
`${addedRoutes.length} route(s) added:`,
|
|
814
|
+
`${addedRoutes.length} route(s) added from ${directory}:`,
|
|
752
815
|
addedRoutes.map((r) => r.path)
|
|
753
816
|
);
|
|
754
|
-
addedRoutes.forEach((route) =>
|
|
817
|
+
addedRoutes.forEach((route) => {
|
|
818
|
+
const finalRoute = prefix ? { ...route, path: `${prefix}${route.path}` } : route;
|
|
819
|
+
addRouteWithSource(finalRoute, source);
|
|
820
|
+
});
|
|
755
821
|
},
|
|
756
822
|
onRouteChanged: (changedRoutes) => {
|
|
757
823
|
console.log(
|
|
758
|
-
`${changedRoutes.length} route(s) changed:`,
|
|
824
|
+
`${changedRoutes.length} route(s) changed in ${directory}:`,
|
|
759
825
|
changedRoutes.map((r) => r.path)
|
|
760
826
|
);
|
|
761
827
|
changedRoutes.forEach((route) => {
|
|
762
|
-
const
|
|
828
|
+
const finalPath = prefix ? `${prefix}${route.path}` : route.path;
|
|
829
|
+
const index = routes.findIndex((r) => r.path === finalPath);
|
|
763
830
|
if (index >= 0) {
|
|
764
831
|
routes.splice(index, 1);
|
|
832
|
+
const sources = routeSources.get(finalPath) || [];
|
|
833
|
+
const filteredSources = sources.filter((s) => s !== source);
|
|
834
|
+
if (filteredSources.length > 0) {
|
|
835
|
+
routeSources.set(finalPath, filteredSources);
|
|
836
|
+
} else {
|
|
837
|
+
routeSources.delete(finalPath);
|
|
838
|
+
}
|
|
765
839
|
}
|
|
766
|
-
|
|
840
|
+
const finalRoute = prefix ? { ...route, path: finalPath } : route;
|
|
841
|
+
addRouteWithSource(finalRoute, source);
|
|
767
842
|
});
|
|
768
843
|
},
|
|
769
844
|
onRouteRemoved: (filePath, removedRoutes) => {
|
|
770
|
-
console.log("-----------------------Routes before removal:", routes);
|
|
771
845
|
console.log(
|
|
772
|
-
`File removed: ${filePath} with ${removedRoutes.length} route(s):`,
|
|
846
|
+
`File removed from ${directory}: ${filePath} with ${removedRoutes.length} route(s):`,
|
|
773
847
|
removedRoutes.map((r) => r.path)
|
|
774
848
|
);
|
|
775
849
|
removedRoutes.forEach((route) => {
|
|
776
|
-
const
|
|
850
|
+
const finalPath = prefix ? `${prefix}${route.path}` : route.path;
|
|
851
|
+
const index = routes.findIndex((r) => r.path === finalPath);
|
|
777
852
|
if (index >= 0) {
|
|
778
853
|
routes.splice(index, 1);
|
|
779
854
|
}
|
|
855
|
+
const sources = routeSources.get(finalPath) || [];
|
|
856
|
+
const filteredSources = sources.filter((s) => s !== source);
|
|
857
|
+
if (filteredSources.length > 0) {
|
|
858
|
+
routeSources.set(finalPath, filteredSources);
|
|
859
|
+
} else {
|
|
860
|
+
routeSources.delete(finalPath);
|
|
861
|
+
}
|
|
780
862
|
});
|
|
781
|
-
console.log("-----------------------Routes after removal:", routes);
|
|
782
863
|
},
|
|
783
864
|
onError: (error) => {
|
|
784
|
-
console.error(
|
|
865
|
+
console.error(`Route watcher error for ${directory}:`, error);
|
|
785
866
|
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
function setupWatcherForDirectory(directory, source, prefix) {
|
|
870
|
+
const callbacks = createWatcherCallbacks(directory, source, prefix);
|
|
871
|
+
const watcher = watchRoutes(directory, {
|
|
872
|
+
ignore: ["node_modules", ".git"],
|
|
873
|
+
...callbacks
|
|
786
874
|
});
|
|
875
|
+
if (!_watchers) {
|
|
876
|
+
_watchers = /* @__PURE__ */ new Map();
|
|
877
|
+
}
|
|
878
|
+
_watchers.set(directory, watcher);
|
|
879
|
+
return watcher;
|
|
880
|
+
}
|
|
881
|
+
function setupWatcherForAllDirectories() {
|
|
882
|
+
for (const directory of routeDirectories) {
|
|
883
|
+
setupWatcherForDirectory(directory, directory);
|
|
884
|
+
}
|
|
787
885
|
}
|
|
788
886
|
initialize().catch((error) => {
|
|
789
887
|
console.error("Failed to initialize router on creation:", error);
|
|
@@ -833,6 +931,34 @@ function createRouter(options) {
|
|
|
833
931
|
*/
|
|
834
932
|
addRoute(route) {
|
|
835
933
|
addRouteInternal(route);
|
|
934
|
+
},
|
|
935
|
+
/**
|
|
936
|
+
* Add a route directory (for plugins)
|
|
937
|
+
*/
|
|
938
|
+
async addRouteDirectory(directory, options2 = {}) {
|
|
939
|
+
if (routeDirectories.has(directory)) {
|
|
940
|
+
console.warn(`Route directory ${directory} already registered`);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
routeDirectories.add(directory);
|
|
944
|
+
if (initialized) {
|
|
945
|
+
await loadRoutesFromDirectory(directory, directory, options2.prefix);
|
|
946
|
+
if (routerOptions.watchMode) {
|
|
947
|
+
setupWatcherForDirectory(directory, directory, options2.prefix);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
/**
|
|
952
|
+
* Get route conflicts
|
|
953
|
+
*/
|
|
954
|
+
getRouteConflicts() {
|
|
955
|
+
const conflicts = [];
|
|
956
|
+
for (const [path5, sources] of routeSources.entries()) {
|
|
957
|
+
if (sources.length > 1) {
|
|
958
|
+
conflicts.push({ path: path5, sources });
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return conflicts;
|
|
836
962
|
}
|
|
837
963
|
};
|
|
838
964
|
}
|
|
@@ -855,7 +981,7 @@ function getCallerFilePath() {
|
|
|
855
981
|
try {
|
|
856
982
|
Error.prepareStackTrace = (_, stack2) => stack2;
|
|
857
983
|
const stack = new Error().stack;
|
|
858
|
-
const callerFrame = stack[
|
|
984
|
+
const callerFrame = stack[3];
|
|
859
985
|
if (!callerFrame || typeof callerFrame.getFileName !== "function") {
|
|
860
986
|
throw new Error("Unable to determine caller file frame");
|
|
861
987
|
}
|
|
@@ -869,9 +995,11 @@ function getCallerFilePath() {
|
|
|
869
995
|
}
|
|
870
996
|
}
|
|
871
997
|
function getRoutePath() {
|
|
998
|
+
console.log("getRoutePath called");
|
|
872
999
|
const callerPath = getCallerFilePath();
|
|
873
1000
|
const routesDir = getRoutesDir();
|
|
874
1001
|
const parsedRoute = parseRoutePath(callerPath, routesDir);
|
|
1002
|
+
console.log(`Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
|
|
875
1003
|
return parsedRoute.routePath;
|
|
876
1004
|
}
|
|
877
1005
|
var createGetRoute = (config2) => {
|
|
@@ -1560,12 +1688,12 @@ async function stopServer(serverInstance, options = {}) {
|
|
|
1560
1688
|
return;
|
|
1561
1689
|
}
|
|
1562
1690
|
const timeout = options.timeout || 3e4;
|
|
1563
|
-
const plugins = serverInstance.plugins;
|
|
1564
1691
|
try {
|
|
1565
1692
|
if (options.onStopping) {
|
|
1566
1693
|
await options.onStopping();
|
|
1567
1694
|
}
|
|
1568
1695
|
events.emit("stopping");
|
|
1696
|
+
await serverInstance.pluginManager.onServerStop(serverInstance, server);
|
|
1569
1697
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1570
1698
|
setTimeout(() => {
|
|
1571
1699
|
reject(new Error("Server shutdown timed out waiting for requests to complete"));
|
|
@@ -1580,13 +1708,7 @@ async function stopServer(serverInstance, options = {}) {
|
|
|
1580
1708
|
});
|
|
1581
1709
|
});
|
|
1582
1710
|
await Promise.race([closePromise, timeoutPromise]);
|
|
1583
|
-
|
|
1584
|
-
for (const plugin of [...plugins].reverse()) {
|
|
1585
|
-
if (plugin.terminate) {
|
|
1586
|
-
await plugin.terminate();
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1711
|
+
await serverInstance.pluginManager.terminatePlugins(serverInstance);
|
|
1590
1712
|
if (options.onStopped) {
|
|
1591
1713
|
await options.onStopped();
|
|
1592
1714
|
}
|
|
@@ -1661,6 +1783,158 @@ function validateServerOptions(options) {
|
|
|
1661
1783
|
}
|
|
1662
1784
|
}
|
|
1663
1785
|
|
|
1786
|
+
// src/plugins/lifecycle.ts
|
|
1787
|
+
function createPluginLifecycleManager(options = {}) {
|
|
1788
|
+
const { continueOnError = true, debug = false, onError } = options;
|
|
1789
|
+
function log(message, ...args) {
|
|
1790
|
+
if (debug) {
|
|
1791
|
+
console.log(`[PluginLifecycle] ${message}`, ...args);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
function handleError(plugin, phase, error) {
|
|
1795
|
+
const errorMessage = `Plugin ${plugin.name} failed during ${phase}: ${error.message}`;
|
|
1796
|
+
if (onError) {
|
|
1797
|
+
onError(plugin, phase, error);
|
|
1798
|
+
} else {
|
|
1799
|
+
console.error(errorMessage, error);
|
|
1800
|
+
}
|
|
1801
|
+
if (!continueOnError) {
|
|
1802
|
+
throw new Error(errorMessage);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
return {
|
|
1806
|
+
/**
|
|
1807
|
+
* Initialize all plugins
|
|
1808
|
+
*/
|
|
1809
|
+
async initializePlugins(server) {
|
|
1810
|
+
log("Initializing plugins...");
|
|
1811
|
+
for (const plugin of server.plugins) {
|
|
1812
|
+
if (plugin.initialize) {
|
|
1813
|
+
try {
|
|
1814
|
+
log(`Initializing plugin: ${plugin.name}`);
|
|
1815
|
+
await plugin.initialize(server);
|
|
1816
|
+
} catch (error) {
|
|
1817
|
+
handleError(plugin, "initialize", error);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
log(`Initialized ${server.plugins.length} plugins`);
|
|
1822
|
+
},
|
|
1823
|
+
/**
|
|
1824
|
+
* Terminate all plugins in reverse order
|
|
1825
|
+
*/
|
|
1826
|
+
async terminatePlugins(server) {
|
|
1827
|
+
log("Terminating plugins...");
|
|
1828
|
+
const pluginsToTerminate = [...server.plugins].reverse();
|
|
1829
|
+
for (const plugin of pluginsToTerminate) {
|
|
1830
|
+
if (plugin.terminate) {
|
|
1831
|
+
try {
|
|
1832
|
+
log(`Terminating plugin: ${plugin.name}`);
|
|
1833
|
+
await plugin.terminate(server);
|
|
1834
|
+
} catch (error) {
|
|
1835
|
+
handleError(plugin, "terminate", error);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
log(`Terminated ${pluginsToTerminate.length} plugins`);
|
|
1840
|
+
},
|
|
1841
|
+
/**
|
|
1842
|
+
* Notify plugins that the server has started
|
|
1843
|
+
*/
|
|
1844
|
+
async onServerStart(server, httpServer) {
|
|
1845
|
+
log("Notifying plugins of server start...");
|
|
1846
|
+
for (const plugin of server.plugins) {
|
|
1847
|
+
if (plugin.onServerStart) {
|
|
1848
|
+
try {
|
|
1849
|
+
log(`Notifying plugin of server start: ${plugin.name}`);
|
|
1850
|
+
await plugin.onServerStart(httpServer);
|
|
1851
|
+
} catch (error) {
|
|
1852
|
+
handleError(plugin, "onServerStart", error);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
/**
|
|
1858
|
+
* Notify plugins that the server is stopping
|
|
1859
|
+
*/
|
|
1860
|
+
async onServerStop(server, httpServer) {
|
|
1861
|
+
log("Notifying plugins of server stop...");
|
|
1862
|
+
const pluginsToNotify = [...server.plugins].reverse();
|
|
1863
|
+
for (const plugin of pluginsToNotify) {
|
|
1864
|
+
if (plugin.onServerStop) {
|
|
1865
|
+
try {
|
|
1866
|
+
log(`Notifying plugin of server stop: ${plugin.name}`);
|
|
1867
|
+
await plugin.onServerStop(httpServer);
|
|
1868
|
+
} catch (error) {
|
|
1869
|
+
handleError(plugin, "onServerStop", error);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// src/plugins/errors.ts
|
|
1878
|
+
var PluginValidationError = class extends Error {
|
|
1879
|
+
constructor(pluginName, message) {
|
|
1880
|
+
super(`Plugin validation error${pluginName ? ` for "${pluginName}"` : ""}: ${message}`);
|
|
1881
|
+
this.pluginName = pluginName;
|
|
1882
|
+
this.name = "PluginValidationError";
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
// src/plugins/validation.ts
|
|
1887
|
+
var RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
1888
|
+
"core",
|
|
1889
|
+
"server",
|
|
1890
|
+
"router",
|
|
1891
|
+
"middleware",
|
|
1892
|
+
"context",
|
|
1893
|
+
"blaize",
|
|
1894
|
+
"blaizejs"
|
|
1895
|
+
]);
|
|
1896
|
+
var VALID_NAME_PATTERN = /^[a-z]([a-z0-9-]*[a-z0-9])?$/;
|
|
1897
|
+
var VALID_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9-.]+)?(?:\+[a-zA-Z0-9-.]+)?$/;
|
|
1898
|
+
function validatePlugin(plugin, options = {}) {
|
|
1899
|
+
const { requireVersion = true, validateNameFormat = true, checkReservedNames = true } = options;
|
|
1900
|
+
if (!plugin || typeof plugin !== "object") {
|
|
1901
|
+
throw new PluginValidationError("", "Plugin must be an object");
|
|
1902
|
+
}
|
|
1903
|
+
const p = plugin;
|
|
1904
|
+
if (!p.name || typeof p.name !== "string") {
|
|
1905
|
+
throw new PluginValidationError("", "Plugin must have a name (string)");
|
|
1906
|
+
}
|
|
1907
|
+
if (validateNameFormat && !VALID_NAME_PATTERN.test(p.name)) {
|
|
1908
|
+
throw new PluginValidationError(
|
|
1909
|
+
p.name,
|
|
1910
|
+
"Plugin name must be lowercase letters, numbers, and hyphens only"
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
if (checkReservedNames && RESERVED_NAMES.has(p.name.toLowerCase())) {
|
|
1914
|
+
throw new PluginValidationError(p.name, `Plugin name "${p.name}" is reserved`);
|
|
1915
|
+
}
|
|
1916
|
+
if (requireVersion) {
|
|
1917
|
+
if (!p.version || typeof p.version !== "string") {
|
|
1918
|
+
throw new PluginValidationError(p.name, "Plugin must have a version (string)");
|
|
1919
|
+
}
|
|
1920
|
+
if (!VALID_VERSION_PATTERN.test(p.version)) {
|
|
1921
|
+
throw new PluginValidationError(
|
|
1922
|
+
p.name,
|
|
1923
|
+
'Plugin version must follow semantic versioning (e.g., "1.0.0")'
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
if (!p.register || typeof p.register !== "function") {
|
|
1928
|
+
throw new PluginValidationError(p.name, "Plugin must have a register method (function)");
|
|
1929
|
+
}
|
|
1930
|
+
const lifecycleMethods = ["initialize", "terminate", "onServerStart", "onServerStop"];
|
|
1931
|
+
for (const method of lifecycleMethods) {
|
|
1932
|
+
if (p[method] && typeof p[method] !== "function") {
|
|
1933
|
+
throw new PluginValidationError(p.name, `Plugin ${method} must be a function if provided`);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1664
1938
|
// src/server/create.ts
|
|
1665
1939
|
var DEFAULT_OPTIONS = {
|
|
1666
1940
|
port: 3e3,
|
|
@@ -1691,7 +1965,9 @@ function createServerOptions(options = {}) {
|
|
|
1691
1965
|
function createListenMethod(serverInstance, validatedOptions, initialMiddleware, initialPlugins) {
|
|
1692
1966
|
return async () => {
|
|
1693
1967
|
await initializeComponents(serverInstance, initialMiddleware, initialPlugins);
|
|
1968
|
+
await serverInstance.pluginManager.initializePlugins(serverInstance);
|
|
1694
1969
|
await startServer(serverInstance, validatedOptions);
|
|
1970
|
+
await serverInstance.pluginManager.onServerStart(serverInstance, serverInstance.server);
|
|
1695
1971
|
setupServerLifecycle(serverInstance);
|
|
1696
1972
|
return serverInstance;
|
|
1697
1973
|
};
|
|
@@ -1737,13 +2013,6 @@ function createRegisterMethod(serverInstance) {
|
|
|
1737
2013
|
return serverInstance;
|
|
1738
2014
|
};
|
|
1739
2015
|
}
|
|
1740
|
-
function validatePlugin(plugin) {
|
|
1741
|
-
if (!plugin || typeof plugin !== "object" || !("register" in plugin) || typeof plugin.register !== "function") {
|
|
1742
|
-
throw new Error(
|
|
1743
|
-
"Invalid plugin. Must be a valid BlaizeJS plugin object with a register method."
|
|
1744
|
-
);
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
2016
|
function create3(options = {}) {
|
|
1748
2017
|
const mergedOptions = createServerOptions(options);
|
|
1749
2018
|
let validatedOptions;
|
|
@@ -1763,6 +2032,10 @@ function create3(options = {}) {
|
|
|
1763
2032
|
routesDir: validatedOptions.routesDir,
|
|
1764
2033
|
watchMode: process.env.NODE_ENV === "development"
|
|
1765
2034
|
});
|
|
2035
|
+
const pluginManager = createPluginLifecycleManager({
|
|
2036
|
+
debug: process.env.NODE_ENV === "development",
|
|
2037
|
+
continueOnError: true
|
|
2038
|
+
});
|
|
1766
2039
|
const events = new import_node_events.default();
|
|
1767
2040
|
const serverInstance = {
|
|
1768
2041
|
server: null,
|
|
@@ -1779,7 +2052,8 @@ function create3(options = {}) {
|
|
|
1779
2052
|
listen: async () => serverInstance,
|
|
1780
2053
|
close: async () => {
|
|
1781
2054
|
},
|
|
1782
|
-
router
|
|
2055
|
+
router,
|
|
2056
|
+
pluginManager
|
|
1783
2057
|
};
|
|
1784
2058
|
serverInstance.listen = createListenMethod(
|
|
1785
2059
|
serverInstance,
|