@harpy-js/core 0.4.7
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/README.md +326 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +53 -0
- package/dist/client/Link.d.ts +5 -0
- package/dist/client/Link.js +62 -0
- package/dist/client/__tests__/getActiveItemId.test.d.ts +1 -0
- package/dist/client/__tests__/getActiveItemId.test.js +38 -0
- package/dist/client/getActiveItemId.d.ts +7 -0
- package/dist/client/getActiveItemId.js +55 -0
- package/dist/client/use-i18n.d.ts +7 -0
- package/dist/client/use-i18n.js +64 -0
- package/dist/core/__tests__/component-analyzer.test.d.ts +1 -0
- package/dist/core/__tests__/component-analyzer.test.js +151 -0
- package/dist/core/__tests__/hydration-manifest.test.d.ts +1 -0
- package/dist/core/__tests__/hydration-manifest.test.js +211 -0
- package/dist/core/__tests__/jsx.engine.test.d.ts +1 -0
- package/dist/core/__tests__/jsx.engine.test.js +118 -0
- package/dist/core/app-setup.d.ts +7 -0
- package/dist/core/app-setup.js +79 -0
- package/dist/core/auto-register.module.d.ts +9 -0
- package/dist/core/auto-register.module.js +18 -0
- package/dist/core/auto-wrap-middleware.d.ts +4 -0
- package/dist/core/auto-wrap-middleware.js +130 -0
- package/dist/core/client-component-wrapper.d.ts +5 -0
- package/dist/core/client-component-wrapper.js +37 -0
- package/dist/core/client-hydration.d.ts +2 -0
- package/dist/core/client-hydration.js +93 -0
- package/dist/core/client-wrapper-browser.d.ts +2 -0
- package/dist/core/client-wrapper-browser.js +22 -0
- package/dist/core/component-analyzer.d.ts +4 -0
- package/dist/core/component-analyzer.js +98 -0
- package/dist/core/component-auto-wrapper.d.ts +2 -0
- package/dist/core/component-auto-wrapper.js +63 -0
- package/dist/core/component-client-wrapper.d.ts +4 -0
- package/dist/core/component-client-wrapper.js +80 -0
- package/dist/core/hydration-generator.d.ts +2 -0
- package/dist/core/hydration-generator.js +98 -0
- package/dist/core/hydration-manifest.d.ts +7 -0
- package/dist/core/hydration-manifest.js +83 -0
- package/dist/core/hydration.d.ts +16 -0
- package/dist/core/hydration.js +72 -0
- package/dist/core/jsx.engine.d.ts +9 -0
- package/dist/core/jsx.engine.js +161 -0
- package/dist/core/live-reload-client.js +32 -0
- package/dist/core/live-reload.controller.d.ts +10 -0
- package/dist/core/live-reload.controller.js +38 -0
- package/dist/core/navigation.service.d.ts +18 -0
- package/dist/core/navigation.service.js +206 -0
- package/dist/core/router.module.d.ts +2 -0
- package/dist/core/router.module.js +21 -0
- package/dist/core/static-assets.controller.d.ts +4 -0
- package/dist/core/static-assets.controller.js +51 -0
- package/dist/core/types/nav.types.d.ts +22 -0
- package/dist/core/types/nav.types.js +2 -0
- package/dist/core/views/layout.d.ts +8 -0
- package/dist/core/views/layout.js +35 -0
- package/dist/decorators/jsx.decorator.d.ts +26 -0
- package/dist/decorators/jsx.decorator.js +10 -0
- package/dist/decorators/layout.decorator.d.ts +4 -0
- package/dist/decorators/layout.decorator.js +29 -0
- package/dist/i18n/__tests__/i18n.helper.test.d.ts +1 -0
- package/dist/i18n/__tests__/i18n.helper.test.js +105 -0
- package/dist/i18n/__tests__/i18n.interceptor.test.d.ts +1 -0
- package/dist/i18n/__tests__/i18n.interceptor.test.js +195 -0
- package/dist/i18n/__tests__/i18n.module.test.d.ts +1 -0
- package/dist/i18n/__tests__/i18n.module.test.js +83 -0
- package/dist/i18n/__tests__/i18n.service.test.d.ts +1 -0
- package/dist/i18n/__tests__/i18n.service.test.js +109 -0
- package/dist/i18n/__tests__/t.test.d.ts +1 -0
- package/dist/i18n/__tests__/t.test.js +66 -0
- package/dist/i18n/i18n-module.options.d.ts +10 -0
- package/dist/i18n/i18n-module.options.js +4 -0
- package/dist/i18n/i18n-switcher.controller.d.ts +12 -0
- package/dist/i18n/i18n-switcher.controller.js +80 -0
- package/dist/i18n/i18n-types.d.ts +8 -0
- package/dist/i18n/i18n-types.js +2 -0
- package/dist/i18n/i18n.helper.d.ts +14 -0
- package/dist/i18n/i18n.helper.js +70 -0
- package/dist/i18n/i18n.interceptor.d.ts +9 -0
- package/dist/i18n/i18n.interceptor.js +99 -0
- package/dist/i18n/i18n.module.d.ts +5 -0
- package/dist/i18n/i18n.module.js +51 -0
- package/dist/i18n/i18n.service.d.ts +12 -0
- package/dist/i18n/i18n.service.js +61 -0
- package/dist/i18n/index.d.ts +10 -0
- package/dist/i18n/index.js +20 -0
- package/dist/i18n/locale.decorator.d.ts +1 -0
- package/dist/i18n/locale.decorator.js +8 -0
- package/dist/i18n/t.d.ts +3 -0
- package/dist/i18n/t.js +16 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +40 -0
- package/package.json +79 -0
- package/scripts/analyze-styles.ts +124 -0
- package/scripts/auto-wrap-exports.ts +239 -0
- package/scripts/build-css.ts +38 -0
- package/scripts/build-hydration.ts +313 -0
- package/scripts/build-page-styles.ts +43 -0
- package/scripts/copy-assets.ts +34 -0
- package/scripts/dev.sh +3 -0
- package/scripts/dev.ts +257 -0
- package/src/cli.ts +71 -0
- package/src/client/Link.tsx +62 -0
- package/src/client/__tests__/getActiveItemId.test.ts +49 -0
- package/src/client/getActiveItemId.ts +54 -0
- package/src/client/use-i18n.ts +111 -0
- package/src/core/__tests__/component-analyzer.test.ts +141 -0
- package/src/core/__tests__/hydration-manifest.test.ts +223 -0
- package/src/core/__tests__/jsx.engine.test.ts +137 -0
- package/src/core/app-setup.ts +114 -0
- package/src/core/auto-register.module.ts +30 -0
- package/src/core/auto-wrap-middleware.ts +165 -0
- package/src/core/client-component-wrapper.ts +72 -0
- package/src/core/client-hydration.tsx +99 -0
- package/src/core/client-wrapper-browser.ts +40 -0
- package/src/core/component-analyzer.ts +89 -0
- package/src/core/component-auto-wrapper.ts +68 -0
- package/src/core/component-client-wrapper.ts +112 -0
- package/src/core/hydration-generator.ts +94 -0
- package/src/core/hydration-manifest.ts +79 -0
- package/src/core/hydration.ts +70 -0
- package/src/core/jsx.engine.ts +205 -0
- package/src/core/live-reload-client.js +32 -0
- package/src/core/live-reload.controller.ts +55 -0
- package/src/core/navigation.service.ts +257 -0
- package/src/core/router.module.ts +9 -0
- package/src/core/static-assets.controller.ts +19 -0
- package/src/core/types/nav.types.ts +53 -0
- package/src/core/views/layout.tsx +61 -0
- package/src/decorators/jsx.decorator.ts +49 -0
- package/src/decorators/layout.decorator.ts +66 -0
- package/src/i18n/__tests__/i18n.helper.test.ts +126 -0
- package/src/i18n/__tests__/i18n.interceptor.test.ts +229 -0
- package/src/i18n/__tests__/i18n.module.test.ts +98 -0
- package/src/i18n/__tests__/i18n.service.test.ts +129 -0
- package/src/i18n/__tests__/t.test.ts +88 -0
- package/src/i18n/i18n-module.options.ts +53 -0
- package/src/i18n/i18n-switcher.controller.ts +99 -0
- package/src/i18n/i18n-types.ts +56 -0
- package/src/i18n/i18n.helper.ts +75 -0
- package/src/i18n/i18n.interceptor.ts +114 -0
- package/src/i18n/i18n.module.ts +45 -0
- package/src/i18n/i18n.service.ts +95 -0
- package/src/i18n/index.ts +37 -0
- package/src/i18n/locale.decorator.ts +10 -0
- package/src/i18n/t.ts +62 -0
- package/src/index.ts +31 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getHydrationManifest = getHydrationManifest;
|
|
37
|
+
exports.getChunkFileName = getChunkFileName;
|
|
38
|
+
exports.getChunkPath = getChunkPath;
|
|
39
|
+
exports.invalidateManifestCache = invalidateManifestCache;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
let cachedManifest = null;
|
|
43
|
+
function getHydrationManifest() {
|
|
44
|
+
if (cachedManifest) {
|
|
45
|
+
return cachedManifest;
|
|
46
|
+
}
|
|
47
|
+
const manifestPaths = [
|
|
48
|
+
path.join(process.cwd(), "dist", "hydration-manifest.json"),
|
|
49
|
+
path.join(process.cwd(), "src", "hydration-manifest.json"),
|
|
50
|
+
];
|
|
51
|
+
let manifestPath = null;
|
|
52
|
+
for (const p of manifestPaths) {
|
|
53
|
+
if (fs.existsSync(p)) {
|
|
54
|
+
manifestPath = p;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!manifestPath) {
|
|
59
|
+
console.warn("[Hydration] Manifest file not found at any of:", manifestPaths, "- ensure build:hydration has been run");
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const content = fs.readFileSync(manifestPath, "utf-8");
|
|
64
|
+
cachedManifest = JSON.parse(content);
|
|
65
|
+
return cachedManifest;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error("[Hydration] Failed to load hydration manifest:", error);
|
|
69
|
+
cachedManifest = {};
|
|
70
|
+
return cachedManifest;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getChunkFileName(componentName) {
|
|
74
|
+
const manifest = getHydrationManifest();
|
|
75
|
+
return manifest[componentName] || null;
|
|
76
|
+
}
|
|
77
|
+
function getChunkPath(componentName) {
|
|
78
|
+
const fileName = getChunkFileName(componentName);
|
|
79
|
+
return fileName ? `/chunks/${fileName}` : null;
|
|
80
|
+
}
|
|
81
|
+
function invalidateManifestCache() {
|
|
82
|
+
cachedManifest = null;
|
|
83
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2
|
+
export interface ClientComponentInstance {
|
|
3
|
+
componentPath: string;
|
|
4
|
+
componentName: string;
|
|
5
|
+
instanceId: string;
|
|
6
|
+
props: Record<string, any>;
|
|
7
|
+
}
|
|
8
|
+
export interface HydrationContext {
|
|
9
|
+
clientComponents: Map<string, ClientComponentInstance>;
|
|
10
|
+
}
|
|
11
|
+
export declare const hydrationContext: AsyncLocalStorage<HydrationContext>;
|
|
12
|
+
export declare function generateInstanceId(componentPath: string): string;
|
|
13
|
+
export declare function initializeHydrationContext(): HydrationContext;
|
|
14
|
+
export declare function registerClientComponent(instance: ClientComponentInstance): void;
|
|
15
|
+
export declare function getClientComponents(): ClientComponentInstance[];
|
|
16
|
+
export declare function clearHydrationContext(): void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.hydrationContext = void 0;
|
|
37
|
+
exports.generateInstanceId = generateInstanceId;
|
|
38
|
+
exports.initializeHydrationContext = initializeHydrationContext;
|
|
39
|
+
exports.registerClientComponent = registerClientComponent;
|
|
40
|
+
exports.getClientComponents = getClientComponents;
|
|
41
|
+
exports.clearHydrationContext = clearHydrationContext;
|
|
42
|
+
const async_hooks_1 = require("async_hooks");
|
|
43
|
+
const crypto = __importStar(require("crypto"));
|
|
44
|
+
exports.hydrationContext = new async_hooks_1.AsyncLocalStorage();
|
|
45
|
+
function generateInstanceId(componentPath) {
|
|
46
|
+
return `${crypto.randomBytes(4).toString("hex")}-${Date.now()}`;
|
|
47
|
+
}
|
|
48
|
+
function initializeHydrationContext() {
|
|
49
|
+
const context = {
|
|
50
|
+
clientComponents: new Map(),
|
|
51
|
+
};
|
|
52
|
+
return context;
|
|
53
|
+
}
|
|
54
|
+
function registerClientComponent(instance) {
|
|
55
|
+
const context = exports.hydrationContext.getStore();
|
|
56
|
+
if (context) {
|
|
57
|
+
context.clientComponents.set(instance.instanceId, instance);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function getClientComponents() {
|
|
61
|
+
const context = exports.hydrationContext.getStore();
|
|
62
|
+
if (!context) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
return Array.from(context.clientComponents.values());
|
|
66
|
+
}
|
|
67
|
+
function clearHydrationContext() {
|
|
68
|
+
const context = exports.hydrationContext.getStore();
|
|
69
|
+
if (context) {
|
|
70
|
+
context.clientComponents.clear();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NestFastifyApplication } from "@nestjs/platform-fastify";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { MetaOptions } from "../decorators/jsx.decorator";
|
|
4
|
+
export interface JsxLayoutProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
meta?: MetaOptions;
|
|
7
|
+
}
|
|
8
|
+
export type JsxLayout = (props: JsxLayoutProps) => React.ReactElement;
|
|
9
|
+
export declare function withJsxEngine(app: NestFastifyApplication, defaultLayout: JsxLayout): void;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.withJsxEngine = withJsxEngine;
|
|
37
|
+
const React = __importStar(require("react"));
|
|
38
|
+
const server_1 = require("react-dom/server");
|
|
39
|
+
const hydration_1 = require("./hydration");
|
|
40
|
+
const hydration_manifest_1 = require("./hydration-manifest");
|
|
41
|
+
const live_reload_controller_1 = require("./live-reload.controller");
|
|
42
|
+
const static_assets_controller_1 = require("./static-assets.controller");
|
|
43
|
+
const chunkPathCache = new Map();
|
|
44
|
+
function initializeChunkCache() {
|
|
45
|
+
const manifest = (0, hydration_manifest_1.getHydrationManifest)();
|
|
46
|
+
Object.keys(manifest).forEach((componentName) => {
|
|
47
|
+
const path = (0, hydration_manifest_1.getChunkPath)(componentName);
|
|
48
|
+
if (path) {
|
|
49
|
+
chunkPathCache.set(componentName, path);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
console.log(`[JSX Engine] Preloaded ${chunkPathCache.size} component chunk mappings`);
|
|
53
|
+
}
|
|
54
|
+
function withJsxEngine(app, defaultLayout) {
|
|
55
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
56
|
+
initializeChunkCache();
|
|
57
|
+
if (isDev) {
|
|
58
|
+
const httpAdapter = app.getHttpAdapter();
|
|
59
|
+
const liveReloadController = new live_reload_controller_1.LiveReloadController();
|
|
60
|
+
const staticAssetsController = new static_assets_controller_1.StaticAssetsController();
|
|
61
|
+
httpAdapter.get("/__harpy/live-reload", (req, reply) => {
|
|
62
|
+
liveReloadController.liveReload(reply);
|
|
63
|
+
});
|
|
64
|
+
httpAdapter.post("/__harpy/live-reload/trigger", (req, reply) => {
|
|
65
|
+
liveReloadController.notifyReload();
|
|
66
|
+
reply.send({ status: "ok" });
|
|
67
|
+
});
|
|
68
|
+
httpAdapter.get("/__harpy/live-reload.js", (req, reply) => {
|
|
69
|
+
staticAssetsController.liveReloadScript(reply);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
app.getHttpAdapter().render = async function (reply, view, options) {
|
|
73
|
+
const res = reply.raw;
|
|
74
|
+
if (reply.statusCode >= 300) {
|
|
75
|
+
res.end();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const [component, controllerOpts] = view;
|
|
79
|
+
const layout = controllerOpts.layout ?? defaultLayout;
|
|
80
|
+
const props = {
|
|
81
|
+
...options,
|
|
82
|
+
};
|
|
83
|
+
let meta = undefined;
|
|
84
|
+
if (typeof controllerOpts.meta === "function") {
|
|
85
|
+
try {
|
|
86
|
+
meta = await controllerOpts.meta(reply.request, props);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
console.error("Error resolving dynamic meta:", e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
meta = controllerOpts.meta;
|
|
94
|
+
}
|
|
95
|
+
const layoutProps = {
|
|
96
|
+
...props,
|
|
97
|
+
meta,
|
|
98
|
+
};
|
|
99
|
+
let html;
|
|
100
|
+
if (layout) {
|
|
101
|
+
layoutProps.children = React.createElement(component, props);
|
|
102
|
+
html = React.createElement(layout, layoutProps);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
html = React.createElement(component, props);
|
|
106
|
+
}
|
|
107
|
+
const hydrationCtx = (0, hydration_1.initializeHydrationContext)();
|
|
108
|
+
global.__COMPONENT_REGISTRY__ = (data) => {
|
|
109
|
+
hydrationCtx.clientComponents.set(data.instanceId, data);
|
|
110
|
+
};
|
|
111
|
+
const startTime = Date.now();
|
|
112
|
+
let htmlString = "";
|
|
113
|
+
hydration_1.hydrationContext.run(hydrationCtx, () => {
|
|
114
|
+
try {
|
|
115
|
+
htmlString = (0, server_1.renderToString)(html);
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
console.error("[JSX Engine] Render error:", e.message?.split("\n")[0]);
|
|
119
|
+
throw e;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
const registeredComponents = Array.from(hydrationCtx.clientComponents.values());
|
|
123
|
+
const uniqueComponentNames = new Set(registeredComponents.map((c) => c.componentName));
|
|
124
|
+
const hydrationScripts = Array.from(uniqueComponentNames)
|
|
125
|
+
.map((componentName) => {
|
|
126
|
+
const path = chunkPathCache.get(componentName);
|
|
127
|
+
if (!path) {
|
|
128
|
+
const livePath = (0, hydration_manifest_1.getChunkPath)(componentName);
|
|
129
|
+
if (livePath) {
|
|
130
|
+
chunkPathCache.set(componentName, livePath);
|
|
131
|
+
return { componentName, path: livePath };
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return { componentName, path };
|
|
136
|
+
})
|
|
137
|
+
.filter((script) => script !== null);
|
|
138
|
+
const renderTime = Date.now() - startTime;
|
|
139
|
+
if (isDev) {
|
|
140
|
+
console.log(`[JSX Engine] Rendered in ${renderTime}ms with ${hydrationScripts.length} scripts for:`, Array.from(uniqueComponentNames).join(", "));
|
|
141
|
+
}
|
|
142
|
+
let hydrationScriptsHtml = "";
|
|
143
|
+
if (hydrationScripts.length > 0) {
|
|
144
|
+
hydrationScriptsHtml = '<script src="/chunks/vendor.js"></script>';
|
|
145
|
+
hydrationScripts.forEach((script) => {
|
|
146
|
+
hydrationScriptsHtml += `<script src="${script.path}"></script>`;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (isDev) {
|
|
150
|
+
const liveReloadScript = '<script src="/__harpy/live-reload.js"></script>';
|
|
151
|
+
const scriptsToInject = `${hydrationScriptsHtml}${liveReloadScript}`;
|
|
152
|
+
htmlString = htmlString.replace("</body>", `${scriptsToInject}</body>`);
|
|
153
|
+
}
|
|
154
|
+
else if (hydrationScriptsHtml) {
|
|
155
|
+
htmlString = htmlString.replace("</body>", `${hydrationScriptsHtml}</body>`);
|
|
156
|
+
}
|
|
157
|
+
res.setHeader("content-type", "text/html");
|
|
158
|
+
reply.status(reply.statusCode || 200);
|
|
159
|
+
res.end(htmlString);
|
|
160
|
+
};
|
|
161
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harpy Live Reload Client
|
|
3
|
+
* Connects to the dev server via SSE and reloads the page when changes are detected
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
if (typeof window === "undefined") return;
|
|
7
|
+
|
|
8
|
+
const eventSource = new EventSource("/__harpy/live-reload");
|
|
9
|
+
|
|
10
|
+
eventSource.onmessage = (event) => {
|
|
11
|
+
try {
|
|
12
|
+
const data = JSON.parse(event.data);
|
|
13
|
+
if (data.type === "reload") {
|
|
14
|
+
console.log("[Harpy] Reloading page...");
|
|
15
|
+
window.location.reload();
|
|
16
|
+
} else if (data.type === "connected") {
|
|
17
|
+
console.log("[Harpy] Live reload connected");
|
|
18
|
+
}
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error("[Harpy] Failed to parse message:", err);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
eventSource.onerror = () => {
|
|
25
|
+
console.log("[Harpy] Live reload disconnected, retrying...");
|
|
26
|
+
eventSource.close();
|
|
27
|
+
// Retry connection after 1 second
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
window.location.reload();
|
|
30
|
+
}, 1000);
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LiveReloadController = void 0;
|
|
4
|
+
class LiveReloadController {
|
|
5
|
+
clients = [];
|
|
6
|
+
lastReloadTime = Date.now();
|
|
7
|
+
liveReload(reply) {
|
|
8
|
+
reply.raw.writeHead(200, {
|
|
9
|
+
"Content-Type": "text/event-stream",
|
|
10
|
+
"Cache-Control": "no-cache",
|
|
11
|
+
Connection: "keep-alive",
|
|
12
|
+
});
|
|
13
|
+
this.clients.push(reply);
|
|
14
|
+
reply.raw.write(`data: ${JSON.stringify({ type: "connected" })}\n\n`);
|
|
15
|
+
reply.raw.on("close", () => {
|
|
16
|
+
const index = this.clients.indexOf(reply);
|
|
17
|
+
if (index !== -1) {
|
|
18
|
+
this.clients.splice(index, 1);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
triggerReload() {
|
|
23
|
+
this.notifyReload();
|
|
24
|
+
return { success: true };
|
|
25
|
+
}
|
|
26
|
+
notifyReload() {
|
|
27
|
+
this.lastReloadTime = Date.now();
|
|
28
|
+
const message = `data: ${JSON.stringify({ type: "reload", timestamp: this.lastReloadTime })}\n\n`;
|
|
29
|
+
this.clients.forEach((client) => {
|
|
30
|
+
try {
|
|
31
|
+
client.raw.write(message);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.LiveReloadController = LiveReloadController;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { NavItem, NavSection, NavigationRegistry } from "./types/nav.types";
|
|
2
|
+
export declare class NavigationService implements NavigationRegistry {
|
|
3
|
+
private sections;
|
|
4
|
+
private topLevelItems;
|
|
5
|
+
private cachedSections;
|
|
6
|
+
private dirty;
|
|
7
|
+
private hrefIndex;
|
|
8
|
+
constructor();
|
|
9
|
+
registerSection(section: NavSection): void;
|
|
10
|
+
addItemToSection(sectionId: string, item: NavItem): void;
|
|
11
|
+
registerItem(item: NavItem): void;
|
|
12
|
+
getAllSections(): NavSection[];
|
|
13
|
+
private ensureCache;
|
|
14
|
+
getActiveItemId(currentPath?: string): string | undefined;
|
|
15
|
+
getSectionsForRoute(currentPath?: string): NavSection[];
|
|
16
|
+
getSection(sectionId: string): NavSection | undefined;
|
|
17
|
+
moveSectionToFront(sectionId: string): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.NavigationService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
let NavigationService = class NavigationService {
|
|
15
|
+
sections = new Map();
|
|
16
|
+
topLevelItems = [];
|
|
17
|
+
cachedSections = null;
|
|
18
|
+
dirty = true;
|
|
19
|
+
hrefIndex = new Map();
|
|
20
|
+
constructor() { }
|
|
21
|
+
registerSection(section) {
|
|
22
|
+
this.sections.set(section.id, section);
|
|
23
|
+
}
|
|
24
|
+
addItemToSection(sectionId, item) {
|
|
25
|
+
let section = this.sections.get(sectionId);
|
|
26
|
+
if (!section) {
|
|
27
|
+
const humanize = (id) => id.replace(/[-_/]+/g, " ").replace(/(^|\s)\S/g, (s) => s.toUpperCase());
|
|
28
|
+
section = {
|
|
29
|
+
id: sectionId,
|
|
30
|
+
title: humanize(sectionId),
|
|
31
|
+
items: [],
|
|
32
|
+
};
|
|
33
|
+
this.registerSection(section);
|
|
34
|
+
}
|
|
35
|
+
section.items.push(item);
|
|
36
|
+
}
|
|
37
|
+
registerItem(item) {
|
|
38
|
+
this.topLevelItems.push(item);
|
|
39
|
+
this.dirty = true;
|
|
40
|
+
}
|
|
41
|
+
getAllSections() {
|
|
42
|
+
if (!this.dirty && this.cachedSections) {
|
|
43
|
+
return this.cachedSections.map((s) => ({ ...s, items: s.items.slice() }));
|
|
44
|
+
}
|
|
45
|
+
const sectionsList = Array.from(this.sections.values());
|
|
46
|
+
if (this.topLevelItems.length > 0) {
|
|
47
|
+
sectionsList.unshift({
|
|
48
|
+
id: "__top__",
|
|
49
|
+
title: "",
|
|
50
|
+
items: this.topLevelItems.slice(),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const arr = sectionsList.map((s, idx) => ({
|
|
54
|
+
section: s,
|
|
55
|
+
idx,
|
|
56
|
+
order: s.id === "__top__"
|
|
57
|
+
? Number.NEGATIVE_INFINITY
|
|
58
|
+
: typeof s.order === "number"
|
|
59
|
+
? s.order
|
|
60
|
+
: Number.POSITIVE_INFINITY,
|
|
61
|
+
}));
|
|
62
|
+
arr.sort((a, b) => {
|
|
63
|
+
if (a.order === b.order)
|
|
64
|
+
return a.idx - b.idx;
|
|
65
|
+
return a.order - b.order;
|
|
66
|
+
});
|
|
67
|
+
const built = arr.map((x) => {
|
|
68
|
+
const s = x.section;
|
|
69
|
+
const itemsWithMeta = s.items.map((it, i) => ({
|
|
70
|
+
item: it,
|
|
71
|
+
idx: i,
|
|
72
|
+
order: typeof it.order === "number" ? it.order : Number.POSITIVE_INFINITY,
|
|
73
|
+
}));
|
|
74
|
+
itemsWithMeta.sort((u, v) => {
|
|
75
|
+
if (u.order === v.order)
|
|
76
|
+
return u.idx - v.idx;
|
|
77
|
+
return u.order - v.order;
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
id: s.id,
|
|
81
|
+
title: s.title,
|
|
82
|
+
order: s.order,
|
|
83
|
+
items: itemsWithMeta.map((m) => m.item),
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
this.hrefIndex.clear();
|
|
87
|
+
const normalize = (p) => {
|
|
88
|
+
if (!p)
|
|
89
|
+
return "";
|
|
90
|
+
const withoutQuery = p.split(/[?#]/)[0];
|
|
91
|
+
if (withoutQuery.length > 1 && withoutQuery.endsWith("/"))
|
|
92
|
+
return withoutQuery.slice(0, -1);
|
|
93
|
+
return withoutQuery;
|
|
94
|
+
};
|
|
95
|
+
for (const s of built) {
|
|
96
|
+
for (const it of s.items) {
|
|
97
|
+
if (!it.href)
|
|
98
|
+
continue;
|
|
99
|
+
const key = normalize(it.href);
|
|
100
|
+
if (!this.hrefIndex.has(key))
|
|
101
|
+
this.hrefIndex.set(key, []);
|
|
102
|
+
this.hrefIndex
|
|
103
|
+
.get(key)
|
|
104
|
+
.push({
|
|
105
|
+
sectionId: s.id === "__top__" ? undefined : s.id,
|
|
106
|
+
itemId: it.id,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
this.cachedSections = built;
|
|
111
|
+
this.dirty = false;
|
|
112
|
+
return built.map((s) => ({ ...s, items: s.items.slice() }));
|
|
113
|
+
}
|
|
114
|
+
ensureCache() {
|
|
115
|
+
if (this.dirty)
|
|
116
|
+
this.getAllSections();
|
|
117
|
+
}
|
|
118
|
+
getActiveItemId(currentPath) {
|
|
119
|
+
if (!currentPath)
|
|
120
|
+
return undefined;
|
|
121
|
+
this.ensureCache();
|
|
122
|
+
const normalize = (p) => {
|
|
123
|
+
if (!p)
|
|
124
|
+
return "";
|
|
125
|
+
const withoutQuery = p.split(/[?#]/)[0];
|
|
126
|
+
if (withoutQuery.length > 1 && withoutQuery.endsWith("/"))
|
|
127
|
+
return withoutQuery.slice(0, -1);
|
|
128
|
+
return withoutQuery;
|
|
129
|
+
};
|
|
130
|
+
let cur = normalize(currentPath);
|
|
131
|
+
while (cur !== "") {
|
|
132
|
+
const entry = this.hrefIndex.get(cur);
|
|
133
|
+
if (entry && entry.length > 0)
|
|
134
|
+
return entry[0].itemId;
|
|
135
|
+
const lastSlash = cur.lastIndexOf("/");
|
|
136
|
+
if (lastSlash === -1)
|
|
137
|
+
break;
|
|
138
|
+
if (lastSlash === 0) {
|
|
139
|
+
cur = "/";
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
cur = cur.slice(0, lastSlash);
|
|
143
|
+
}
|
|
144
|
+
if (cur === "/") {
|
|
145
|
+
const entryRoot = this.hrefIndex.get("/");
|
|
146
|
+
if (entryRoot && entryRoot.length > 0)
|
|
147
|
+
return entryRoot[0].itemId;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
getSectionsForRoute(currentPath) {
|
|
154
|
+
const normalize = (p) => {
|
|
155
|
+
if (!p)
|
|
156
|
+
return "";
|
|
157
|
+
const withoutQuery = p.split(/[?#]/)[0];
|
|
158
|
+
if (withoutQuery.length > 1 && withoutQuery.endsWith("/")) {
|
|
159
|
+
return withoutQuery.slice(0, -1);
|
|
160
|
+
}
|
|
161
|
+
return withoutQuery;
|
|
162
|
+
};
|
|
163
|
+
const matches = (itemHref, cur) => {
|
|
164
|
+
if (!itemHref || !cur)
|
|
165
|
+
return false;
|
|
166
|
+
const a = normalize(itemHref);
|
|
167
|
+
const b = normalize(cur);
|
|
168
|
+
if (!a)
|
|
169
|
+
return false;
|
|
170
|
+
if (a === b)
|
|
171
|
+
return true;
|
|
172
|
+
return b.startsWith(a + "/");
|
|
173
|
+
};
|
|
174
|
+
const base = this.getAllSections();
|
|
175
|
+
if (!currentPath)
|
|
176
|
+
return base;
|
|
177
|
+
const activeId = this.getActiveItemId(currentPath);
|
|
178
|
+
if (!activeId)
|
|
179
|
+
return base;
|
|
180
|
+
return base.map((s) => ({
|
|
181
|
+
...s,
|
|
182
|
+
items: s.items.map((it) => ({ ...it, active: it.id === activeId })),
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
getSection(sectionId) {
|
|
186
|
+
return this.sections.get(sectionId);
|
|
187
|
+
}
|
|
188
|
+
moveSectionToFront(sectionId) {
|
|
189
|
+
const sec = this.sections.get(sectionId);
|
|
190
|
+
if (!sec)
|
|
191
|
+
return;
|
|
192
|
+
const newMap = new Map();
|
|
193
|
+
newMap.set(sectionId, sec);
|
|
194
|
+
for (const [k, v] of this.sections.entries()) {
|
|
195
|
+
if (k === sectionId)
|
|
196
|
+
continue;
|
|
197
|
+
newMap.set(k, v);
|
|
198
|
+
}
|
|
199
|
+
this.sections = newMap;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
exports.NavigationService = NavigationService;
|
|
203
|
+
exports.NavigationService = NavigationService = __decorate([
|
|
204
|
+
(0, common_1.Injectable)(),
|
|
205
|
+
__metadata("design:paramtypes", [])
|
|
206
|
+
], NavigationService);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.RouterModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const navigation_service_1 = require("./navigation.service");
|
|
12
|
+
let RouterModule = class RouterModule {
|
|
13
|
+
};
|
|
14
|
+
exports.RouterModule = RouterModule;
|
|
15
|
+
exports.RouterModule = RouterModule = __decorate([
|
|
16
|
+
(0, common_1.Global)(),
|
|
17
|
+
(0, common_1.Module)({
|
|
18
|
+
providers: [navigation_service_1.NavigationService],
|
|
19
|
+
exports: [navigation_service_1.NavigationService],
|
|
20
|
+
})
|
|
21
|
+
], RouterModule);
|