@ecopages/lit 0.3.0 → 0.3.1
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/package.json +2 -2
- package/src/lit-renderer.d.ts +0 -5
- package/src/lit-renderer.js +0 -4
- package/src/lit-static-render-error.d.ts +17 -0
- package/src/lit-static-render-error.js +34 -0
- package/src/lit-static-render-protocol.d.ts +6 -0
- package/src/lit-static-render-session.d.ts +0 -2
- package/src/lit-static-render-session.js +0 -6
- package/src/lit-static-render-worker-client.js +2 -1
- package/src/lit-static-render-worker.js +2 -9
- package/src/STATIC_RENDER.md +0 -75
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/lit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Ecopages lit integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"directory": "packages/integrations/lit"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@ecopages/core": "0.3.
|
|
45
|
+
"@ecopages/core": "0.3.1",
|
|
46
46
|
"@lit-labs/ssr": "^3.3.0",
|
|
47
47
|
"@lit-labs/ssr-client": "^1.1.7",
|
|
48
48
|
"lit": "^3.2.1"
|
package/src/lit-renderer.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains the Lit renderer hooks.
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
1
|
/**
|
|
6
2
|
* This module contains the Lit renderer hooks.
|
|
7
3
|
* @module
|
|
@@ -10,7 +6,6 @@ import type { EcoPagesElement } from '@ecopages/core';
|
|
|
10
6
|
import { createIntegrationRenderer } from '@ecopages/core/integration';
|
|
11
7
|
import type { LitStaticRenderSession } from './lit-static-render-session.js';
|
|
12
8
|
export type LitRenderer = InstanceType<ReturnType<typeof createLitRenderer>>;
|
|
13
|
-
export type LitRendererOptions = ConstructorParameters<ReturnType<typeof createLitRenderer>>[0];
|
|
14
9
|
/**
|
|
15
10
|
* Creates the Lit renderer class with a lazy worker-session lookup.
|
|
16
11
|
*
|
package/src/lit-renderer.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module contains the Lit renderer hooks.
|
|
3
|
-
* @module
|
|
4
|
-
*/
|
|
5
1
|
import { createIntegrationRenderer } from '@ecopages/core/integration';
|
|
6
2
|
import { resolveQueuedTokens } from '@ecopages/core/integration';
|
|
7
3
|
import { CUSTOM_ELEMENT_SSR_PRELOAD_CACHE_SCOPES, CustomElementScriptPreloader, } from '@ecopages/core/integration';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LitStaticRenderWorkerErrorMessage } from './lit-static-render-protocol.js';
|
|
2
|
+
/**
|
|
3
|
+
* Turns an error thrown in the Lit worker into the message posted to the main thread.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `postMessage` cannot carry an `Error` with its class, so the status of an `HttpError` travels as a field.
|
|
7
|
+
* Without it a page that throws `HttpError.NotFound()` would reach the main thread as a plain error and render as a 500.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toLitStaticRenderErrorMessage(error: unknown, id?: string): LitStaticRenderWorkerErrorMessage;
|
|
10
|
+
/**
|
|
11
|
+
* Rebuilds the error a Lit worker reported, as an `HttpError` when the worker's error carried a status.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The worker-side name, message and stack are kept on `cause` because the rebuilt error's own stack points at the
|
|
15
|
+
* message handler, not at the page that threw.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fromLitStaticRenderErrorMessage(message: LitStaticRenderWorkerErrorMessage): Error;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HttpError } from '@ecopages/core/errors';
|
|
2
|
+
/**
|
|
3
|
+
* Turns an error thrown in the Lit worker into the message posted to the main thread.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `postMessage` cannot carry an `Error` with its class, so the status of an `HttpError` travels as a field.
|
|
7
|
+
* Without it a page that throws `HttpError.NotFound()` would reach the main thread as a plain error and render as a 500.
|
|
8
|
+
*/
|
|
9
|
+
export function toLitStaticRenderErrorMessage(error, id) {
|
|
10
|
+
if (!(error instanceof Error) && !HttpError.isHttpError(error)) {
|
|
11
|
+
return { type: 'error', id, message: String(error) };
|
|
12
|
+
}
|
|
13
|
+
const { message, name, stack } = error;
|
|
14
|
+
return {
|
|
15
|
+
type: 'error',
|
|
16
|
+
id,
|
|
17
|
+
message,
|
|
18
|
+
name,
|
|
19
|
+
stack,
|
|
20
|
+
status: HttpError.isHttpError(error) ? error.status : undefined,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Rebuilds the error a Lit worker reported, as an `HttpError` when the worker's error carried a status.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* The worker-side name, message and stack are kept on `cause` because the rebuilt error's own stack points at the
|
|
28
|
+
* message handler, not at the page that threw.
|
|
29
|
+
*/
|
|
30
|
+
export function fromLitStaticRenderErrorMessage(message) {
|
|
31
|
+
const error = message.status === undefined ? new Error(message.message) : new HttpError(message.status, message.message);
|
|
32
|
+
error.cause = { name: message.name, message: message.message, stack: message.stack };
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
@@ -33,6 +33,12 @@ export type LitStaticRenderWorkerErrorMessage = {
|
|
|
33
33
|
type: 'error';
|
|
34
34
|
id?: string;
|
|
35
35
|
message: string;
|
|
36
|
+
/** Worker-side error name, kept as the rebuilt error's cause. */
|
|
37
|
+
name?: string;
|
|
38
|
+
/** Worker-side stack, kept as the rebuilt error's cause. */
|
|
39
|
+
stack?: string;
|
|
40
|
+
/** Present when the thrown error was an `HttpError`, so the main thread can rebuild one with the same status. */
|
|
41
|
+
status?: number;
|
|
36
42
|
};
|
|
37
43
|
export type LitStaticRenderWorkerShutdownCompleteMessage = {
|
|
38
44
|
type: 'shutdown-complete';
|
|
@@ -51,13 +51,11 @@ export declare class LitStaticRenderSession {
|
|
|
51
51
|
}): Promise<void>;
|
|
52
52
|
preloadStaticRoutes(context: StaticExportContext): Promise<void>;
|
|
53
53
|
preloadSsrScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): Promise<void>;
|
|
54
|
-
collectSsrPreloadScripts(components: Array<CustomElementSsrPreloadComponent | undefined>): string[];
|
|
55
54
|
renderPageInWorker(input: {
|
|
56
55
|
filePath: string;
|
|
57
56
|
params: PageParams;
|
|
58
57
|
query?: PageQuery;
|
|
59
58
|
}): Promise<LitStaticRenderResult>;
|
|
60
|
-
isExpectedSsrPreloadError(error: unknown): boolean;
|
|
61
59
|
dispose(): Promise<void>;
|
|
62
60
|
private preloadLitRoutes;
|
|
63
61
|
private isLitRoute;
|
|
@@ -79,9 +79,6 @@ export class LitStaticRenderSession {
|
|
|
79
79
|
ensureLitDomShim();
|
|
80
80
|
await this.preloader.preloadSsrScripts(components);
|
|
81
81
|
}
|
|
82
|
-
collectSsrPreloadScripts(components) {
|
|
83
|
-
return this.preloader.collectSsrPreloadScripts(components);
|
|
84
|
-
}
|
|
85
82
|
async renderPageInWorker(input) {
|
|
86
83
|
if (!this.workerClient) {
|
|
87
84
|
throw new Error('Lit static render worker is not active');
|
|
@@ -100,9 +97,6 @@ export class LitStaticRenderSession {
|
|
|
100
97
|
this.renderChain = render.then(() => undefined, () => undefined);
|
|
101
98
|
return render;
|
|
102
99
|
}
|
|
103
|
-
isExpectedSsrPreloadError(error) {
|
|
104
|
-
return this.preloader.isExpectedSsrPreloadError(error);
|
|
105
|
-
}
|
|
106
100
|
async dispose() {
|
|
107
101
|
await this.workerClient?.dispose();
|
|
108
102
|
this.workerClient = null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Worker } from 'node:worker_threads';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { fromLitStaticRenderErrorMessage } from './lit-static-render-error.js';
|
|
3
4
|
const workerShutdownTimeoutMs = 30_000;
|
|
4
5
|
const workerExitTimeoutMs = 1_000;
|
|
5
6
|
export class LitStaticRenderWorkerClient {
|
|
@@ -53,7 +54,7 @@ export class LitStaticRenderWorkerClient {
|
|
|
53
54
|
if (message.id && this.pending.has(message.id)) {
|
|
54
55
|
const pending = this.pending.get(message.id);
|
|
55
56
|
this.pending.delete(message.id);
|
|
56
|
-
pending.reject(
|
|
57
|
+
pending.reject(fromLitStaticRenderErrorMessage(message));
|
|
57
58
|
return;
|
|
58
59
|
}
|
|
59
60
|
const error = new Error(message.message);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { parentPort } from 'node:worker_threads';
|
|
2
2
|
import { loadEcoPagesConfig } from '@ecopages/core/config';
|
|
3
3
|
import { setupAppRuntimeIntegrations, setupAppRuntimePlugins, teardownAppRuntimeIntegrations, } from '@ecopages/core/build';
|
|
4
|
-
import { installBuildQueue } from '@ecopages/core/build';
|
|
5
4
|
import { requireAppRuntime } from '@ecopages/core/integration';
|
|
6
5
|
import { RouteRendererFactory } from '@ecopages/core/integration';
|
|
6
|
+
import { toLitStaticRenderErrorMessage } from './lit-static-render-error.js';
|
|
7
7
|
import { toLitStaticRenderResult } from './lit-static-render-result.js';
|
|
8
8
|
import './dom-shim.js';
|
|
9
9
|
let appConfig = null;
|
|
@@ -27,10 +27,8 @@ async function initializeWorker(configModulePath, runtimeOrigin) {
|
|
|
27
27
|
appConfig,
|
|
28
28
|
});
|
|
29
29
|
await setupAppRuntimeIntegrations({ appConfig });
|
|
30
|
-
installBuildQueue(appConfig);
|
|
31
30
|
routeRendererFactory = new RouteRendererFactory({
|
|
32
31
|
appConfig,
|
|
33
|
-
runtime,
|
|
34
32
|
});
|
|
35
33
|
}
|
|
36
34
|
async function renderPage(filePath, params, query) {
|
|
@@ -101,11 +99,6 @@ parentPort.on('message', async (message) => {
|
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
catch (error) {
|
|
104
|
-
|
|
105
|
-
if (message.type === 'render') {
|
|
106
|
-
postMessage({ type: 'error', id: message.id, message: errorMessage });
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
postMessage({ type: 'error', message: errorMessage });
|
|
102
|
+
postMessage(toLitStaticRenderErrorMessage(error, message.type === 'render' ? message.id : undefined));
|
|
110
103
|
}
|
|
111
104
|
});
|
package/src/STATIC_RENDER.md
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# Lit static render worker
|
|
2
|
-
|
|
3
|
-
Lit-first page routes (`.lit.tsx`) render through a dedicated worker thread during static export and runtime when the Lit plugin is active.
|
|
4
|
-
|
|
5
|
-
## Flow
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
Main thread Worker thread
|
|
9
|
-
─────────── ─────────────
|
|
10
|
-
LitPlugin.setup()
|
|
11
|
-
└─ LitStaticRenderSession.ensureWorker()
|
|
12
|
-
└─ LitStaticRenderWorkerClient.start()
|
|
13
|
-
└─ postMessage(init) ──► setupAppRuntimePlugins()
|
|
14
|
-
setupAppRuntimeIntegrations()
|
|
15
|
-
installBuildQueue()
|
|
16
|
-
RouteRendererFactory
|
|
17
|
-
|
|
18
|
-
LitPlugin.getRenderer() (creates it through createRenderer(base) on first use after setup(ctx))
|
|
19
|
-
└─ createLitRenderer(() => renderSession) hook factory closes over lazy accessor
|
|
20
|
-
|
|
21
|
-
IntegrationRenderer.execute() (main)
|
|
22
|
-
└─ ensureIntegrationRuntimeActivated()
|
|
23
|
-
└─ getRenderSession()
|
|
24
|
-
├─ locals or explicit props ─► remote hook declines; core renders in-process
|
|
25
|
-
└─ session.renderPageInWorker()
|
|
26
|
-
└─ postMessage(render) ─────► RouteRendererFactory.execute()
|
|
27
|
-
(no active session in worker)
|
|
28
|
-
Lit's preload hook uses
|
|
29
|
-
importServerModule (no entrypoint resolver)
|
|
30
|
-
|
|
31
|
-
staticExport
|
|
32
|
-
└─ ensureWorker() with the export base URL; matching identity is reused
|
|
33
|
-
└─ a different origin restarts the worker
|
|
34
|
-
└─ preloadStaticRoutes()
|
|
35
|
-
└─ preload SSR lazy scripts for Lit pages
|
|
36
|
-
└─ returns a release that disposes the session once the export ends
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
`ECOPAGES_LIT_STATIC_RENDER_WORKER=true` in the worker environment prevents nested worker creation during `LitPlugin.setup()` and skips processor `.setup()` inside `setupAppRuntimePlugins` (the main thread already prepared artifacts). Integration setup still runs in the worker isolate before `RouteRendererFactory` creates route renderers.
|
|
40
|
-
|
|
41
|
-
## Ownership
|
|
42
|
-
|
|
43
|
-
| Concern | Owner |
|
|
44
|
-
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
-
| Session + worker lifecycle | `LitPlugin` (`renderSession`) |
|
|
46
|
-
| Lazy session lookup | `createLitRenderer()` hook factory via `getRenderSession` |
|
|
47
|
-
| Worker identity | First `{ configModulePath, runtimeOrigin }` passed to `ensureWorker`; same identity is sticky; a later `ensureWorker` with a different identity disposes and recreates |
|
|
48
|
-
|
|
49
|
-
Renderers created before plugin `setup()` still use the worker after setup assigns the session, because `execute` activates the integration runtime then reads the accessor.
|
|
50
|
-
|
|
51
|
-
`staticExport` calls `ensureWorker` with `context.baseUrl` even when setup already created the session. A matching identity is reused; a different export origin replaces the worker before routes are preloaded. Its release disposes the session and clears `renderSession`, so an in-process export leaves no worker thread; renders after the export run in-process.
|
|
52
|
-
|
|
53
|
-
## Locals policy
|
|
54
|
-
|
|
55
|
-
When `RouteRendererOptions.locals` or `RouteRendererOptions.props` is present, the Lit remote-render hook declines the worker path and core continues in-process. Request locals are not sent through the worker protocol (structured-clone risk and request-scoped data), and explicit props are not passed because the worker re-imports the route module. Static export uses the worker only for eligible Lit page routes without locals or explicit props; explicit static views continue in-process.
|
|
56
|
-
|
|
57
|
-
## Files
|
|
58
|
-
|
|
59
|
-
| File | Role |
|
|
60
|
-
| ------------------------------------ | --------------------------------------------------------------- |
|
|
61
|
-
| `lit-static-render-session.ts` | Plugin-runtime-scoped session: worker lifecycle + SSR preload |
|
|
62
|
-
| `lit-static-render-worker-client.ts` | Message protocol client |
|
|
63
|
-
| `lit-static-render-worker.ts` | Worker entry (exported as `@ecopages/lit/static-render-worker`) |
|
|
64
|
-
| `lit-static-render-protocol.ts` | Request/response message types |
|
|
65
|
-
|
|
66
|
-
## Protocol
|
|
67
|
-
|
|
68
|
-
| Message | Direction | Purpose |
|
|
69
|
-
| ------------------- | ------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
70
|
-
| `init` | main → worker | Load `eco.config.ts`, bootstrap runtime (`configModulePath`, `runtimeOrigin`) |
|
|
71
|
-
| `ready` | worker → main | Worker initialized |
|
|
72
|
-
| `render` | main → worker | Render one Lit page route (`filePath`, `PageParams`, optional `PageQuery`) |
|
|
73
|
-
| `result` / `error` | worker → main | HTML body plus optional `cacheStrategy`, `sourceDependencyPaths` and resolved `metadata`, or an error message |
|
|
74
|
-
| `shutdown` | main → worker | Wait for active renders, tear down activated Integrations, and close the worker message port |
|
|
75
|
-
| `shutdown-complete` | worker → main | Confirm Integration teardown completed; the client waits for worker exit and force-terminates after timeout |
|